From c19ed790665ad123d6fb52e79051a68534befe65 Mon Sep 17 00:00:00 2001 From: Ross Allen Date: Wed, 8 Jun 2016 16:36:37 -0700 Subject: [PATCH 01/21] Complete documentation for Notification creation Notifications support several options that were yet to be documented: `buttons`, `description`, and `stack`. Add descriptions for each option and its defaults (where applicable, e.g. .buttons.className). `stack` is technically available on all notification types, but it doesn't make sense to use it on types other than 'error' and 'fatal'. Therefore it's documented only in those two cases. The usage and expected display, like preformatted vs. Markdown, taken from [atom/notifications/lib/notification-element.coffee][1]. [1]: https://github.com/atom/notifications/blob/v0.64.1/lib/notification-element.coffee --- src/notification-manager.coffee | 74 ++++++++++++++++++++++++++++----- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/src/notification-manager.coffee b/src/notification-manager.coffee index 3d8b1895c..7b712e6bf 100644 --- a/src/notification-manager.coffee +++ b/src/notification-manager.coffee @@ -33,8 +33,18 @@ class NotificationManager # # * `message` A {String} message # * `options` (optional) An options {Object} with the following keys: - # * `detail` (optional) A {String} with additional details about the - # notification. + # * `buttons` (optional) An {Array} of {Object} where each {Object} has the + # following options: + # * `className` (optional) {String} additional class name add to the + # button. It will already receive `btn btn-success`. + # * `onClick` (optional) {Function} callback to call when the button is + # clicked. The context will be set to the {NotificationElement} + # instance. + # * `text` {String} inner text for the button + # * `description` (optional) A {String} that will be rendered as Markdown + # to describe the notification. + # * `detail` (optional) A preformatted {String} that will be rendered as + # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display @@ -46,8 +56,18 @@ class NotificationManager # # * `message` A {String} message # * `options` (optional) An options {Object} with the following keys: - # * `detail` (optional) A {String} with additional details about the - # notification. + # * `buttons` (optional) An {Array} of {Object} where each {Object} has the + # following options: + # * `className` (optional) {String} additional class name add to the + # button. It will already receive `btn btn-info`. + # * `onClick` (optional) {Function} callback to call when the button is + # clicked. The context will be set to the {NotificationElement} + # instance. + # * `text` {String} inner text for the button + # * `description` (optional) A {String} that will be rendered as Markdown + # to describe the notification. + # * `detail` (optional) A preformatted {String} that will be rendered as + # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display @@ -59,8 +79,18 @@ class NotificationManager # # * `message` A {String} message # * `options` (optional) An options {Object} with the following keys: - # * `detail` (optional) A {String} with additional details about the - # notification. + # * `buttons` (optional) An {Array} of {Object} where each {Object} has the + # following options: + # * `className` (optional) {String} additional class name add to the + # button. It will already receive `btn btn-warning`. + # * `onClick` (optional) {Function} callback to call when the button is + # clicked. The context will be set to the {NotificationElement} + # instance. + # * `text` {String} inner text for the button + # * `description` (optional) A {String} that will be rendered as Markdown + # to describe the notification. + # * `detail` (optional) A preformatted {String} that will be rendered as + # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display @@ -72,12 +102,24 @@ class NotificationManager # # * `message` A {String} message # * `options` (optional) An options {Object} with the following keys: - # * `detail` (optional) A {String} with additional details about the - # notification. + # * `buttons` (optional) An {Array} of {Object} where each {Object} has the + # following options: + # * `className` (optional) {String} additional class name add to the + # button. It will already receive `btn btn-error`. + # * `onClick` (optional) {Function} callback to call when the button is + # clicked. The context will be set to the {NotificationElement} + # instance. + # * `text` {String} inner text for the button + # * `description` (optional) A {String} that will be rendered as Markdown + # to describe the notification. + # * `detail` (optional) A preformatted {String} that will be rendered as + # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display # in the notification header. Defaults to `'flame'`. + # * `stack` (optional) A preformatted {String} with stack trace information + # describing the location of the error. addError: (message, options) -> @addNotification(new Notification('error', message, options)) @@ -85,12 +127,24 @@ class NotificationManager # # * `message` A {String} message # * `options` (optional) An options {Object} with the following keys: - # * `detail` (optional) A {String} with additional details about the - # notification. + # * `buttons` (optional) An {Array} of {Object} where each {Object} has the + # following options: + # * `className` (optional) {String} additional class name add to the + # button. It will already receive `btn btn-error`. + # * `onClick` (optional) {Function} callback to call when the button is + # clicked. The context will be set to the {NotificationElement} + # instance. + # * `text` {String} inner text for the button + # * `description` (optional) A {String} that will be rendered as Markdown + # to describe the notification. + # * `detail` (optional) A preformatted {String} that will be rendered as + # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display # in the notification header. Defaults to `'bug'`. + # * `stack` (optional) A preformatted {String} with stack trace information + # describing the location of the error. addFatalError: (message, options) -> @addNotification(new Notification('fatal', message, options)) From 86cdc61ff0ac3efd2b077edc58f0d777a3f00d86 Mon Sep 17 00:00:00 2001 From: Ross Allen Date: Tue, 14 Jun 2016 07:34:32 -0700 Subject: [PATCH 02/21] Correctly reference `onDidClick`, not `onClick` --- src/notification-manager.coffee | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/notification-manager.coffee b/src/notification-manager.coffee index 7b712e6bf..aaeb75c15 100644 --- a/src/notification-manager.coffee +++ b/src/notification-manager.coffee @@ -37,9 +37,9 @@ class NotificationManager # following options: # * `className` (optional) {String} additional class name add to the # button. It will already receive `btn btn-success`. - # * `onClick` (optional) {Function} callback to call when the button is - # clicked. The context will be set to the {NotificationElement} - # instance. + # * `onDidClick` (optional) {Function} callback to call when the button + # has been clicked. The context will be set to the + # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A {String} that will be rendered as Markdown # to describe the notification. @@ -60,9 +60,9 @@ class NotificationManager # following options: # * `className` (optional) {String} additional class name add to the # button. It will already receive `btn btn-info`. - # * `onClick` (optional) {Function} callback to call when the button is - # clicked. The context will be set to the {NotificationElement} - # instance. + # * `onDidClick` (optional) {Function} callback to call when the button + # has been clicked. The context will be set to the + # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A {String} that will be rendered as Markdown # to describe the notification. @@ -83,9 +83,9 @@ class NotificationManager # following options: # * `className` (optional) {String} additional class name add to the # button. It will already receive `btn btn-warning`. - # * `onClick` (optional) {Function} callback to call when the button is - # clicked. The context will be set to the {NotificationElement} - # instance. + # * `onDidClick` (optional) {Function} callback to call when the button + # has been clicked. The context will be set to the + # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A {String} that will be rendered as Markdown # to describe the notification. @@ -106,9 +106,9 @@ class NotificationManager # following options: # * `className` (optional) {String} additional class name add to the # button. It will already receive `btn btn-error`. - # * `onClick` (optional) {Function} callback to call when the button is - # clicked. The context will be set to the {NotificationElement} - # instance. + # * `onDidClick` (optional) {Function} callback to call when the button + # has been clicked. The context will be set to the + # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A {String} that will be rendered as Markdown # to describe the notification. @@ -131,9 +131,9 @@ class NotificationManager # following options: # * `className` (optional) {String} additional class name add to the # button. It will already receive `btn btn-error`. - # * `onClick` (optional) {Function} callback to call when the button is - # clicked. The context will be set to the {NotificationElement} - # instance. + # * `onDidClick` (optional) {Function} callback to call when the button + # has been clicked. The context will be set to the + # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A {String} that will be rendered as Markdown # to describe the notification. From 752359b33268ddc924b409f497bb90eab146bbf5 Mon Sep 17 00:00:00 2001 From: Ross Allen Date: Tue, 21 Jun 2016 08:22:05 -0700 Subject: [PATCH 03/21] Clarify `buttonClass` and `description` --- src/notification-manager.coffee | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/notification-manager.coffee b/src/notification-manager.coffee index aaeb75c15..a22ca0256 100644 --- a/src/notification-manager.coffee +++ b/src/notification-manager.coffee @@ -35,14 +35,14 @@ class NotificationManager # * `options` (optional) An options {Object} with the following keys: # * `buttons` (optional) An {Array} of {Object} where each {Object} has the # following options: - # * `className` (optional) {String} additional class name add to the - # button. It will already receive `btn btn-success`. + # * `className` (optional) {String} a class name to add to the button's + # default class name (`btn btn-success`). # * `onDidClick` (optional) {Function} callback to call when the button # has been clicked. The context will be set to the # {NotificationElement} instance. # * `text` {String} inner text for the button - # * `description` (optional) A {String} that will be rendered as Markdown - # to describe the notification. + # * `description` (optional) A Markdown {String} containing a longer + # description about the notification. # * `detail` (optional) A preformatted {String} that will be rendered as # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this @@ -58,14 +58,14 @@ class NotificationManager # * `options` (optional) An options {Object} with the following keys: # * `buttons` (optional) An {Array} of {Object} where each {Object} has the # following options: - # * `className` (optional) {String} additional class name add to the - # button. It will already receive `btn btn-info`. + # * `className` (optional) {String} a class name to add to the button's + # default class name (`btn btn-info`). # * `onDidClick` (optional) {Function} callback to call when the button # has been clicked. The context will be set to the # {NotificationElement} instance. # * `text` {String} inner text for the button - # * `description` (optional) A {String} that will be rendered as Markdown - # to describe the notification. + # * `description` (optional) A Markdown {String} containing a longer + # description about the notification. # * `detail` (optional) A preformatted {String} that will be rendered as # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this @@ -81,14 +81,14 @@ class NotificationManager # * `options` (optional) An options {Object} with the following keys: # * `buttons` (optional) An {Array} of {Object} where each {Object} has the # following options: - # * `className` (optional) {String} additional class name add to the - # button. It will already receive `btn btn-warning`. + # * `className` (optional) {String} a class name to add to the button's + # default class name (`btn btn-warning`). # * `onDidClick` (optional) {Function} callback to call when the button # has been clicked. The context will be set to the # {NotificationElement} instance. # * `text` {String} inner text for the button - # * `description` (optional) A {String} that will be rendered as Markdown - # to describe the notification. + # * `description` (optional) A Markdown {String} containing a longer + # description about the notification. # * `detail` (optional) A preformatted {String} that will be rendered as # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this @@ -104,14 +104,14 @@ class NotificationManager # * `options` (optional) An options {Object} with the following keys: # * `buttons` (optional) An {Array} of {Object} where each {Object} has the # following options: - # * `className` (optional) {String} additional class name add to the - # button. It will already receive `btn btn-error`. + # * `className` (optional) {String} a class name to add to the button's + # default class name (`btn btn-error`). # * `onDidClick` (optional) {Function} callback to call when the button # has been clicked. The context will be set to the # {NotificationElement} instance. # * `text` {String} inner text for the button - # * `description` (optional) A {String} that will be rendered as Markdown - # to describe the notification. + # * `description` (optional) A Markdown {String} containing a longer + # description about the notification. # * `detail` (optional) A preformatted {String} that will be rendered as # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this @@ -129,14 +129,14 @@ class NotificationManager # * `options` (optional) An options {Object} with the following keys: # * `buttons` (optional) An {Array} of {Object} where each {Object} has the # following options: - # * `className` (optional) {String} additional class name add to the - # button. It will already receive `btn btn-error`. + # * `className` (optional) {String} a class name to add to the button's + # default class name (`btn btn-error`). # * `onDidClick` (optional) {Function} callback to call when the button # has been clicked. The context will be set to the # {NotificationElement} instance. # * `text` {String} inner text for the button - # * `description` (optional) A {String} that will be rendered as Markdown - # to describe the notification. + # * `description` (optional) A Markdown {String} containing a longer + # description about the notification. # * `detail` (optional) A preformatted {String} that will be rendered as # plain text with details about the notification. # * `dismissable` (optional) A {Boolean} indicating whether this From 7e75b861d8b9b26382764bea587b676a53c7d8a1 Mon Sep 17 00:00:00 2001 From: Ross Allen Date: Thu, 30 Jun 2016 15:57:22 -0700 Subject: [PATCH 04/21] Describe whitespace in `detail` and `description` `detail` is rendered as preformatted text in core themes. `description` is rendered as Markdown. --- src/notification-manager.coffee | 40 ++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/notification-manager.coffee b/src/notification-manager.coffee index a22ca0256..1cb144bdc 100644 --- a/src/notification-manager.coffee +++ b/src/notification-manager.coffee @@ -42,9 +42,11 @@ class NotificationManager # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A Markdown {String} containing a longer - # description about the notification. - # * `detail` (optional) A preformatted {String} that will be rendered as - # plain text with details about the notification. + # description about the notification. By default, this **will not** + # preserve newlines and whitespace when it is rendered. + # * `detail` (optional) A plain-text {String} containing additional details + # about the notification. By default, this **will** preserve newlines + # and whitespace when it is rendered. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display @@ -65,9 +67,11 @@ class NotificationManager # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A Markdown {String} containing a longer - # description about the notification. - # * `detail` (optional) A preformatted {String} that will be rendered as - # plain text with details about the notification. + # description about the notification. By default, this **will not** + # preserve newlines and whitespace when it is rendered. + # * `detail` (optional) A plain-text {String} containing additional details + # about the notification. By default, this **will** preserve newlines + # and whitespace when it is rendered. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display @@ -88,9 +92,11 @@ class NotificationManager # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A Markdown {String} containing a longer - # description about the notification. - # * `detail` (optional) A preformatted {String} that will be rendered as - # plain text with details about the notification. + # description about the notification. By default, this **will not** + # preserve newlines and whitespace when it is rendered. + # * `detail` (optional) A plain-text {String} containing additional details + # about the notification. By default, this **will** preserve newlines + # and whitespace when it is rendered. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display @@ -111,9 +117,11 @@ class NotificationManager # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A Markdown {String} containing a longer - # description about the notification. - # * `detail` (optional) A preformatted {String} that will be rendered as - # plain text with details about the notification. + # description about the notification. By default, this **will not** + # preserve newlines and whitespace when it is rendered. + # * `detail` (optional) A plain-text {String} containing additional details + # about the notification. By default, this **will** preserve newlines + # and whitespace when it is rendered. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display @@ -136,9 +144,11 @@ class NotificationManager # {NotificationElement} instance. # * `text` {String} inner text for the button # * `description` (optional) A Markdown {String} containing a longer - # description about the notification. - # * `detail` (optional) A preformatted {String} that will be rendered as - # plain text with details about the notification. + # description about the notification. By default, this **will not** + # preserve newlines and whitespace when it is rendered. + # * `detail` (optional) A plain-text {String} containing additional details + # about the notification. By default, this **will** preserve newlines + # and whitespace when it is rendered. # * `dismissable` (optional) A {Boolean} indicating whether this # notification can be dismissed by the user. Defaults to `false`. # * `icon` (optional) A {String} name of an icon from Octicons to display From 4986a64815375e9abe1f080d3f96a6b290b3d222 Mon Sep 17 00:00:00 2001 From: Glavin Wiechert Date: Sat, 2 Jul 2016 16:26:36 -0300 Subject: [PATCH 05/21] Improve incompatible native module error message Close #6771 --- src/package.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/package.coffee b/src/package.coffee index 94e759947..4cb322691 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -426,8 +426,8 @@ class Package return @mainModule if @mainModuleRequired unless @isCompatible() console.warn """ - Failed to require the main module of '#{@name}' because it requires an incompatible native module. - Run `apm rebuild` in the package directory to resolve. + Failed to require the main module of '#{@name}' because it requires an incompatible native module (#{_.map(@incompatibleModules, 'name').join(', ')}). + Run `apm rebuild` in the package directory and restart Atom to resolve. """ return mainModulePath = @getMainModulePath() From 9bf329cef250188c7917ce9d00ebf5bd9db7996f Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 7 Jul 2016 15:53:07 -0700 Subject: [PATCH 06/21] Tear down ipc response handler when atom environment is destroyed Signed-off-by: Nathan Sobo --- src/atom-environment.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 5247ceb97..327b9ea1a 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -224,11 +224,12 @@ class AtomEnvironment extends Model @observeAutoHideMenuBar() - checkPortableHomeWritable = -> + checkPortableHomeWritable = => responseChannel = "check-portable-home-writable-response" ipcRenderer.on responseChannel, (event, response) -> ipcRenderer.removeAllListeners(responseChannel) - atom.notifications.addWarning("#{response.message.replace(/([\\\.+\\-_#!])/g, '\\$1')}") if not response.writable + @notifications.addWarning("#{response.message.replace(/([\\\.+\\-_#!])/g, '\\$1')}") if not response.writable + @disposables.add new Disposable => ipcRenderer.removeAllListeners(responseChannel) ipcRenderer.send('check-portable-home-writable', responseChannel) checkPortableHomeWritable() From d343592fedf5f18e2ff0e23560c6c159ba5e09a3 Mon Sep 17 00:00:00 2001 From: simurai Date: Sat, 9 Jul 2016 16:40:59 +0900 Subject: [PATCH 07/21] :arrow_up: atom-ui@0.4.0 New inputs: https://github.com/atom/atom-ui/pull/8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 29152f4ad..4ae67fcb7 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "async": "0.2.6", "atom-keymap": "6.3.2", - "atom-ui": "0.3.3", + "atom-ui": "0.4.0", "babel-core": "^5.8.21", "cached-run-in-this-context": "0.4.1", "chai": "3.5.0", From 57faec33bb6730a27b06d1bfe9ce32bfd76f3bfa Mon Sep 17 00:00:00 2001 From: simurai Date: Sat, 9 Jul 2016 17:06:41 +0900 Subject: [PATCH 08/21] :arrow_up: styleguide@v0.47.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ae67fcb7..702b7b9a0 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.3.1", - "styleguide": "0.46.0", + "styleguide": "0.47.0", "symbols-view": "0.113.0", "tabs": "0.99.0", "timecop": "0.33.1", From 2f58792577e5a3d9a9551870fd6da1c3118b8596 Mon Sep 17 00:00:00 2001 From: simurai Date: Tue, 12 Jul 2016 15:36:50 +0900 Subject: [PATCH 09/21] Introduce @use-custom-controls variable --- static/variables/ui-variables.less | 1 + 1 file changed, 1 insertion(+) diff --git a/static/variables/ui-variables.less b/static/variables/ui-variables.less index 0a549954d..dd0561932 100644 --- a/static/variables/ui-variables.less +++ b/static/variables/ui-variables.less @@ -83,3 +83,4 @@ // Other @font-family: 'BlinkMacSystemFont', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif; +@use-custom-controls: true; // false uses native controls From 14bcc5f7f2a190b58ecf2e67ed302f1cc52b22c2 Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Tue, 12 Jul 2016 13:59:48 +0200 Subject: [PATCH 10/21] :arrow_up: tree-view@0.208.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 702b7b9a0..e27bc3cd1 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "symbols-view": "0.113.0", "tabs": "0.99.0", "timecop": "0.33.1", - "tree-view": "0.208.0", + "tree-view": "0.208.1", "update-package-dependencies": "0.10.0", "welcome": "0.34.0", "whitespace": "0.32.2", From ce6d45f72a976ea55dc9a0ac5c4de3fde76c46f9 Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Tue, 12 Jul 2016 10:03:55 -0700 Subject: [PATCH 11/21] :art: Fix fat-arrow linter warning --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 327b9ea1a..7c81eaeee 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -229,7 +229,7 @@ class AtomEnvironment extends Model ipcRenderer.on responseChannel, (event, response) -> ipcRenderer.removeAllListeners(responseChannel) @notifications.addWarning("#{response.message.replace(/([\\\.+\\-_#!])/g, '\\$1')}") if not response.writable - @disposables.add new Disposable => ipcRenderer.removeAllListeners(responseChannel) + @disposables.add new Disposable -> ipcRenderer.removeAllListeners(responseChannel) ipcRenderer.send('check-portable-home-writable', responseChannel) checkPortableHomeWritable() From 9f6f9035927e80a4315d470d965ea823b2c78b7d Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 12 Jul 2016 15:31:49 -0700 Subject: [PATCH 12/21] Add enhanced wording @thomasjo recommended --- src/package.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.coffee b/src/package.coffee index 4cb322691..b27e3ce0e 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -426,7 +426,7 @@ class Package return @mainModule if @mainModuleRequired unless @isCompatible() console.warn """ - Failed to require the main module of '#{@name}' because it requires an incompatible native module (#{_.map(@incompatibleModules, 'name').join(', ')}). + Failed to require the main module of '#{@name}' because it requires one or more incompatible native modules (#{_.map(@incompatibleModules, 'name').join(', ')}). Run `apm rebuild` in the package directory and restart Atom to resolve. """ return From cb023016ccfb4ac8a3b5b419f3ba55a7047f9a45 Mon Sep 17 00:00:00 2001 From: simurai Date: Wed, 13 Jul 2016 12:23:47 +0900 Subject: [PATCH 13/21] :arrow_up: atom-ui@0.4.1 Allow themes to opt-out of custom control styling --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e27bc3cd1..e20cac067 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "async": "0.2.6", "atom-keymap": "6.3.2", - "atom-ui": "0.4.0", + "atom-ui": "0.4.1", "babel-core": "^5.8.21", "cached-run-in-this-context": "0.4.1", "chai": "3.5.0", From a4b9e94d7bf69e3f43968b48a556c8a21fc49899 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 13 Jul 2016 08:56:42 -0700 Subject: [PATCH 14/21] :arrow_up: settings-view@0.240.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e20cac067..4cbdd1ff9 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "notifications": "0.65.0", "open-on-github": "1.1.0", "package-generator": "1.0.0", - "settings-view": "0.240.0", + "settings-view": "0.240.1", "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.3.1", From 7b11c31e07af37cdbdf5d9c9b9d30aec6a4b09f5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Jul 2016 14:02:05 -0700 Subject: [PATCH 15/21] Remove synchronous GitRepository's dependency on GitRepositoryAsync Signed-off-by: Nathan Sobo --- spec/git-repository-async-spec.js | 2 +- spec/git-spec.coffee | 30 ---------- src/git-repository-provider.coffee | 2 +- src/git-repository.coffee | 83 ++++++++-------------------- src/repository-status-handler.coffee | 19 ++++++- 5 files changed, 43 insertions(+), 93 deletions(-) diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js index 8b824bab3..4165e258e 100644 --- a/spec/git-repository-async-spec.js +++ b/spec/git-repository-async-spec.js @@ -33,7 +33,7 @@ function copySubmoduleRepository () { return workingDirectory } -describe('GitRepositoryAsync', () => { +fdescribe('GitRepositoryAsync', () => { let repo afterEach(() => { diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index a9de506a2..21624c452 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -259,36 +259,6 @@ describe "GitRepository", -> expect(repo.isStatusModified(status)).toBe false expect(repo.isStatusNew(status)).toBe false - it 'caches the proper statuses when multiple project are open', -> - otherWorkingDirectory = copyRepository() - - atom.project.setPaths([workingDirectory, otherWorkingDirectory]) - - waitsForPromise -> - atom.workspace.open('b.txt') - - statusHandler = null - runs -> - repo = atom.project.getRepositories()[0] - - statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatuses statusHandler - repo.refreshStatus() - - waitsFor -> - statusHandler.callCount > 0 - - runs -> - subDir = path.join(workingDirectory, 'dir') - fs.mkdirSync(subDir) - - filePath = path.join(subDir, 'b.txt') - fs.writeFileSync(filePath, '') - - status = repo.getCachedPathStatus(filePath) - expect(repo.isStatusModified(status)).toBe true - expect(repo.isStatusNew(status)).toBe false - it 'caches statuses that were looked up synchronously', -> originalContent = 'undefined' fs.writeFileSync(modifiedPath, 'making this path modified') diff --git a/src/git-repository-provider.coffee b/src/git-repository-provider.coffee index 463e2bda2..593324d0c 100644 --- a/src/git-repository-provider.coffee +++ b/src/git-repository-provider.coffee @@ -77,7 +77,7 @@ class GitRepositoryProvider unless repo repo = GitRepository.open(gitDirPath, {@project, @config}) return null unless repo - repo.async.onDidDestroy(=> delete @pathToRepository[gitDirPath]) + repo.onDidDestroy(=> delete @pathToRepository[gitDirPath]) @pathToRepository[gitDirPath] = repo repo.refreshIndex() repo.refreshStatus() diff --git a/src/git-repository.coffee b/src/git-repository.coffee index f6bacb760..6f0851cf9 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -83,12 +83,11 @@ class GitRepository asyncOptions.subscribeToBuffers = false @async = GitRepositoryAsync.open(path, asyncOptions) + @statuses = {} @upstream = {ahead: 0, behind: 0} for submodulePath, submoduleRepo of @repo.submodules submoduleRepo.upstream = {ahead: 0, behind: 0} - @statusesByPath = {} - {@project, @config, refreshOnWindowFocus} = options refreshOnWindowFocus ?= true @@ -126,14 +125,6 @@ class GitRepository @subscriptions.dispose() @subscriptions = null - if @async? - @async.destroy() - @async = null - - # Public: Returns a {Boolean} indicating if this repository has been destroyed. - isDestroyed: -> - not @repo? - # Public: Invoke the given callback when this GitRepository's destroy() method # is invoked. # @@ -322,7 +313,7 @@ class GitRepository getDirectoryStatus: (directoryPath) -> directoryPath = "#{@relativize(directoryPath)}/" directoryStatus = 0 - for path, status of Object.assign({}, @async.getCachedPathStatuses(), @statusesByPath) + for path, status of @statuses directoryStatus |= status if path.indexOf(directoryPath) is 0 directoryStatus @@ -335,24 +326,13 @@ class GitRepository getPathStatus: (path) -> repo = @getRepo(path) relativePath = @relativize(path) - - # This is a bit particular. If a package calls `getPathStatus` like this: - # - change the file - # - getPathStatus - # - change the file - # - getPathStatus - # We need to preserve the guarantee that each call to `getPathStatus` will - # synchronously emit 'did-change-status'. So we need to keep a cache of the - # statuses found from this call. - currentPathStatus = @getCachedRelativePathStatus(relativePath) ? 0 - - # Trigger events emitted on the async repo as well - @async.refreshStatusForPath(path) - + currentPathStatus = @statuses[relativePath] ? 0 pathStatus = repo.getStatus(repo.relativize(path)) ? 0 pathStatus = 0 if repo.isStatusIgnored(pathStatus) - @statusesByPath[relativePath] = pathStatus - + if pathStatus > 0 + @statuses[relativePath] = pathStatus + else + delete @statuses[relativePath] if currentPathStatus isnt pathStatus @emitter.emit 'did-change-status', {path, pathStatus} @@ -364,11 +344,7 @@ class GitRepository # # Returns a status {Number} or null if the path is not in the cache. getCachedPathStatus: (path) -> - relativePath = @relativize(path) - @getCachedRelativePathStatus(relativePath) - - getCachedRelativePathStatus: (relativePath) -> - @statusesByPath[relativePath] ? @async.getCachedPathStatuses()[relativePath] + @statuses[@relativize(path)] # Public: Returns true if the given status indicates modification. # @@ -492,42 +468,29 @@ class GitRepository # Refreshes the current git status in an outside process and asynchronously # updates the relevant properties. - # - # Returns a promise that resolves when the repository has been refreshed. refreshStatus: -> - statusesChanged = false + @handlerPath ?= require.resolve('./repository-status-handler') - # Listen for `did-change-statuses` so we know if something changed. But we - # need to wait to propagate it until after we've set the branch and cleared - # the `statusesByPath` cache. So just set a flag, and we'll emit the event - # after refresh is done. - subscription = @async.onDidChangeStatuses -> - subscription?.dispose() - subscription = null + relativeProjectPaths = @project?.getPaths() + .map (path) => @relativize(path) + .filter (path) -> path.length > 0 - statusesChanged = true + @statusTask?.terminate() + new Promise (resolve) => + @statusTask = Task.once @handlerPath, @getPath(), relativeProjectPaths, ({statuses, upstream, branch, submodules}) => + statusesUnchanged = _.isEqual(statuses, @statuses) and + _.isEqual(upstream, @upstream) and + _.isEqual(branch, @branch) and + _.isEqual(submodules, @submodules) - asyncRefresh = @async.refreshStatus().then => - subscription?.dispose() - subscription = null - - @branch = @async?.branch - @statusesByPath = {} - - if statusesChanged - @emitter.emit 'did-change-statuses' - - syncRefresh = new Promise (resolve, reject) => - @handlerPath ?= require.resolve('./repository-status-handler') - - @statusTask?.terminate() - @statusTask = Task.once @handlerPath, @getPath(), ({upstream, submodules}) => + @statuses = statuses @upstream = upstream + @branch = branch @submodules = submodules for submodulePath, submoduleRepo of @getRepo().submodules submoduleRepo.upstream = submodules[submodulePath]?.upstream ? {ahead: 0, behind: 0} + unless statusesUnchanged + @emitter.emit 'did-change-statuses' resolve() - - return Promise.all([asyncRefresh, syncRefresh]) diff --git a/src/repository-status-handler.coffee b/src/repository-status-handler.coffee index adae7bc4f..2fda9a335 100644 --- a/src/repository-status-handler.coffee +++ b/src/repository-status-handler.coffee @@ -5,15 +5,32 @@ module.exports = (repoPath, paths = []) -> repo = Git.open(repoPath) upstream = {} + statuses = {} submodules = {} + branch = null if repo? + # Statuses in main repo + workingDirectoryPath = repo.getWorkingDirectory() + repoStatus = (if paths.length > 0 then repo.getStatusForPaths(paths) else repo.getStatus()) + for filePath, status of repoStatus + statuses[filePath] = status + + # Statuses in submodules for submodulePath, submoduleRepo of repo.submodules submodules[submodulePath] = branch: submoduleRepo.getHead() upstream: submoduleRepo.getAheadBehindCount() + workingDirectoryPath = submoduleRepo.getWorkingDirectory() + for filePath, status of submoduleRepo.getStatus() + absolutePath = path.join(workingDirectoryPath, filePath) + # Make path relative to parent repository + relativePath = repo.relativize(absolutePath) + statuses[relativePath] = status + upstream = repo.getAheadBehindCount() + branch = repo.getHead() repo.release() - {upstream, submodules} + {statuses, upstream, branch, submodules} From 58e789249297adb99350fad38ed10d2513a47c23 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Jul 2016 14:03:29 -0700 Subject: [PATCH 16/21] Remove GitRepositoryAsync Signed-off-by: Nathan Sobo --- exports/atom.coffee | 1 - package.json | 1 - spec/git-repository-async-spec.js | 918 ------------------------------ spec/git-spec.coffee | 10 - src/git-repository-async.js | 558 ------------------ src/git-repository.coffee | 8 - 6 files changed, 1496 deletions(-) delete mode 100644 spec/git-repository-async-spec.js delete mode 100644 src/git-repository-async.js diff --git a/exports/atom.coffee b/exports/atom.coffee index 4953d3756..81d1726b8 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -8,7 +8,6 @@ module.exports = BufferedNodeProcess: require '../src/buffered-node-process' BufferedProcess: require '../src/buffered-process' GitRepository: require '../src/git-repository' - GitRepositoryAsync: require '../src/git-repository-async' Notification: require '../src/notification' TextBuffer: TextBuffer Point: Point diff --git a/package.json b/package.json index 4cbdd1ff9..186c22ecc 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "mocha": "2.5.1", "normalize-package-data": "^2.0.0", "nslog": "^3", - "ohnogit": "0.0.13", "oniguruma": "^5", "pathwatcher": "~6.5", "property-accessors": "^1.1.3", diff --git a/spec/git-repository-async-spec.js b/spec/git-repository-async-spec.js deleted file mode 100644 index 4165e258e..000000000 --- a/spec/git-repository-async-spec.js +++ /dev/null @@ -1,918 +0,0 @@ -'use babel' - -import fs from 'fs-plus' -import path from 'path' -import temp from 'temp' - -import {it, beforeEach, afterEach} from './async-spec-helpers' - -import GitRepositoryAsync from '../src/git-repository-async' -import Project from '../src/project' - -temp.track() - -function openFixture (fixture) { - return GitRepositoryAsync.open(path.join(__dirname, 'fixtures', 'git', fixture)) -} - -function copyRepository (name = 'working-dir') { - const workingDirPath = temp.mkdirSync('atom-working-dir') - fs.copySync(path.join(__dirname, 'fixtures', 'git', name), workingDirPath) - fs.renameSync(path.join(workingDirPath, 'git.git'), path.join(workingDirPath, '.git')) - return fs.realpathSync(workingDirPath) -} - -function copySubmoduleRepository () { - const workingDirectory = copyRepository('repo-with-submodules') - const reGit = (name) => { - fs.renameSync(path.join(workingDirectory, name, 'git.git'), path.join(workingDirectory, name, '.git')) - } - reGit('jstips') - reGit('You-Dont-Need-jQuery') - - return workingDirectory -} - -fdescribe('GitRepositoryAsync', () => { - let repo - - afterEach(() => { - if (repo != null) repo.destroy() - }) - - describe('@open(path)', () => { - it('should throw when no repository is found', async () => { - repo = GitRepositoryAsync.open(path.join(temp.dir, 'nogit.txt')) - - let threw = false - try { - await repo.getRepo() - } catch (e) { - threw = true - } - - expect(threw).toBe(true) - }) - }) - - describe('openedPath', () => { - it('is the path passed to .open', () => { - const workingDirPath = copyRepository() - repo = GitRepositoryAsync.open(workingDirPath) - expect(repo.openedPath).toBe(workingDirPath) - }) - }) - - describe('.getRepo()', () => { - beforeEach(() => { - const workingDirectory = copySubmoduleRepository() - repo = GitRepositoryAsync.open(workingDirectory) - waitsForPromise(() => repo.refreshStatus()) - }) - - it('returns the repository when not given a path', async () => { - const nodeGitRepo1 = await repo.getRepo() - const nodeGitRepo2 = await repo.getRepo() - expect(nodeGitRepo1.workdir()).toBe(nodeGitRepo2.workdir()) - }) - - it('returns the repository when given a non-submodule path', async () => { - const nodeGitRepo1 = await repo.getRepo() - const nodeGitRepo2 = await repo.getRepo('README') - expect(nodeGitRepo1.workdir()).toBe(nodeGitRepo2.workdir()) - }) - - it('returns the submodule repository when given a submodule path', async () => { - const nodeGitRepo1 = await repo.getRepo() - const nodeGitRepo2 = await repo.getRepo('jstips') - expect(nodeGitRepo1.workdir()).not.toBe(nodeGitRepo2.workdir()) - - const nodeGitRepo3 = await repo.getRepo('jstips/README.md') - expect(nodeGitRepo1.workdir()).not.toBe(nodeGitRepo3.workdir()) - expect(nodeGitRepo2.workdir()).toBe(nodeGitRepo3.workdir()) - }) - }) - - describe('.openRepository()', () => { - it('returns a new repository instance', async () => { - repo = openFixture('master.git') - - const originalRepo = await repo.getRepo() - expect(originalRepo).not.toBeNull() - - const nodeGitRepo = repo.openRepository() - expect(nodeGitRepo).not.toBeNull() - expect(originalRepo).not.toBe(nodeGitRepo) - }) - }) - - describe('.getPath()', () => { - it('returns the repository path for a repository path', async () => { - repo = openFixture('master.git') - const repoPath = await repo.getPath() - expect(repoPath).toEqualPath(path.join(__dirname, 'fixtures', 'git', 'master.git')) - }) - }) - - describe('.isPathIgnored(path)', () => { - beforeEach(() => { - repo = openFixture('ignore.git') - }) - - it('resolves true for an ignored path', async () => { - const ignored = await repo.isPathIgnored('a.txt') - expect(ignored).toBe(true) - }) - - it('resolves false for a non-ignored path', async () => { - const ignored = await repo.isPathIgnored('b.txt') - expect(ignored).toBe(false) - }) - }) - - describe('.isPathModified(path)', () => { - let filePath, newPath, emptyPath - - beforeEach(() => { - const workingDirPath = copyRepository() - repo = GitRepositoryAsync.open(workingDirPath) - filePath = path.join(workingDirPath, 'a.txt') - newPath = path.join(workingDirPath, 'new-path.txt') - fs.writeFileSync(newPath, "i'm new here") - emptyPath = path.join(workingDirPath, 'empty-path.txt') - }) - - describe('when the path is unstaged', () => { - it('resolves false if the path has not been modified', async () => { - const modified = await repo.isPathModified(filePath) - expect(modified).toBe(false) - }) - - it('resolves true if the path is modified', async () => { - fs.writeFileSync(filePath, 'change') - const modified = await repo.isPathModified(filePath) - expect(modified).toBe(true) - }) - - it('resolves false if the path is new', async () => { - const modified = await repo.isPathModified(newPath) - expect(modified).toBe(false) - }) - - it('resolves false if the path is invalid', async () => { - const modified = await repo.isPathModified(emptyPath) - expect(modified).toBe(false) - }) - }) - }) - - describe('.isPathNew(path)', () => { - let newPath - - beforeEach(() => { - const workingDirPath = copyRepository() - repo = GitRepositoryAsync.open(workingDirPath) - newPath = path.join(workingDirPath, 'new-path.txt') - fs.writeFileSync(newPath, "i'm new here") - }) - - describe('when the path is unstaged', () => { - it('returns true if the path is new', async () => { - const isNew = await repo.isPathNew(newPath) - expect(isNew).toBe(true) - }) - - it("returns false if the path isn't new", async () => { - const modified = await repo.isPathModified(newPath) - expect(modified).toBe(false) - }) - }) - }) - - describe('.checkoutHead(path)', () => { - let filePath - - beforeEach(() => { - const workingDirPath = copyRepository() - repo = GitRepositoryAsync.open(workingDirPath) - filePath = path.join(workingDirPath, 'a.txt') - }) - - it('no longer reports a path as modified after checkout', async () => { - let modified = await repo.isPathModified(filePath) - expect(modified).toBe(false) - - fs.writeFileSync(filePath, 'ch ch changes') - - modified = await repo.isPathModified(filePath) - expect(modified).toBe(true) - - await repo.checkoutHead(filePath) - - modified = await repo.isPathModified(filePath) - expect(modified).toBe(false) - }) - - it('restores the contents of the path to the original text', async () => { - fs.writeFileSync(filePath, 'ch ch changes') - await repo.checkoutHead(filePath) - expect(fs.readFileSync(filePath, 'utf8')).toBe('') - }) - - it('fires a did-change-status event if the checkout completes successfully', async () => { - fs.writeFileSync(filePath, 'ch ch changes') - - await repo.getPathStatus(filePath) - - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatus(statusHandler) - - await repo.checkoutHead(filePath) - - expect(statusHandler.callCount).toBe(1) - expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: 0}) - - await repo.checkoutHead(filePath) - expect(statusHandler.callCount).toBe(1) - }) - }) - - describe('.checkoutHeadForEditor(editor)', () => { - let filePath - let editor - - beforeEach(async () => { - spyOn(atom, 'confirm') - - const workingDirPath = copyRepository() - repo = new GitRepositoryAsync(workingDirPath, {project: atom.project, config: atom.config, confirm: atom.confirm}) - filePath = path.join(workingDirPath, 'a.txt') - fs.writeFileSync(filePath, 'ch ch changes') - - editor = await atom.workspace.open(filePath) - }) - - it('displays a confirmation dialog by default', async () => { - atom.confirm.andCallFake(({buttons}) => buttons.OK()) - atom.config.set('editor.confirmCheckoutHeadRevision', true) - - await repo.checkoutHeadForEditor(editor) - - expect(fs.readFileSync(filePath, 'utf8')).toBe('') - }) - - it('does not display a dialog when confirmation is disabled', async () => { - atom.config.set('editor.confirmCheckoutHeadRevision', false) - - await repo.checkoutHeadForEditor(editor) - - expect(fs.readFileSync(filePath, 'utf8')).toBe('') - expect(atom.confirm).not.toHaveBeenCalled() - }) - }) - - describe('.destroy()', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('throws an exception when any method is called after it is called', async () => { - repo.destroy() - - let error = null - try { - await repo.getShortHead() - } catch (e) { - error = e - } - - expect(error.name).toBe(GitRepositoryAsync.DestroyedErrorName) - - repo = null - }) - }) - - describe('.getPathStatus(path)', () => { - let filePath - - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - filePath = path.join(workingDirectory, 'file.txt') - }) - - it('trigger a status-changed event when the new status differs from the last cached one', async () => { - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatus(statusHandler) - fs.writeFileSync(filePath, '') - - await repo.getPathStatus(filePath) - - expect(statusHandler.callCount).toBe(1) - const status = GitRepositoryAsync.Git.Status.STATUS.WT_MODIFIED - expect(statusHandler.argsForCall[0][0]).toEqual({path: filePath, pathStatus: status}) - fs.writeFileSync(filePath, 'abc') - - await repo.getPathStatus(filePath) - expect(statusHandler.callCount).toBe(1) - }) - }) - - describe('.getDirectoryStatus(path)', () => { - let directoryPath, filePath - - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - directoryPath = path.join(workingDirectory, 'dir') - filePath = path.join(directoryPath, 'b.txt') - }) - - it('gets the status based on the files inside the directory', async () => { - await repo.checkoutHead(filePath) - - let result = await repo.getDirectoryStatus(directoryPath) - expect(repo.isStatusModified(result)).toBe(false) - - fs.writeFileSync(filePath, 'abc') - - result = await repo.getDirectoryStatus(directoryPath) - expect(repo.isStatusModified(result)).toBe(true) - }) - }) - - describe('.refreshStatus()', () => { - let newPath, modifiedPath, cleanPath, workingDirectory - - beforeEach(() => { - workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - modifiedPath = path.join(workingDirectory, 'file.txt') - newPath = path.join(workingDirectory, 'untracked.txt') - cleanPath = path.join(workingDirectory, 'other.txt') - fs.writeFileSync(cleanPath, 'Full of text') - fs.writeFileSync(newPath, '') - fs.writeFileSync(modifiedPath, 'making this path modified') - newPath = fs.absolute(newPath) // specs could be running under symbol path. - }) - - it('returns status information for all new and modified files', async () => { - await repo.refreshStatus() - - expect(await repo.getCachedPathStatus(cleanPath)).toBeUndefined() - expect(repo.isStatusNew(await repo.getCachedPathStatus(newPath))).toBe(true) - expect(repo.isStatusModified(await repo.getCachedPathStatus(modifiedPath))).toBe(true) - }) - - describe('in a repository with submodules', () => { - beforeEach(() => { - workingDirectory = copySubmoduleRepository() - repo = GitRepositoryAsync.open(workingDirectory) - modifiedPath = path.join(workingDirectory, 'jstips', 'README.md') - newPath = path.join(workingDirectory, 'You-Dont-Need-jQuery', 'untracked.txt') - cleanPath = path.join(workingDirectory, 'jstips', 'CONTRIBUTING.md') - fs.writeFileSync(newPath, '') - fs.writeFileSync(modifiedPath, 'making this path modified') - newPath = fs.absolute(newPath) // specs could be running under symbol path. - }) - - it('returns status information for all new and modified files', async () => { - await repo.refreshStatus() - - expect(await repo.getCachedPathStatus(cleanPath)).toBeUndefined() - expect(repo.isStatusNew(await repo.getCachedPathStatus(newPath))).toBe(true) - expect(repo.isStatusModified(await repo.getCachedPathStatus(modifiedPath))).toBe(true) - }) - }) - - it('caches the proper statuses when a subdir is open', async () => { - const subDir = path.join(workingDirectory, 'dir') - fs.mkdirSync(subDir) - - const filePath = path.join(subDir, 'b.txt') - fs.writeFileSync(filePath, '') - - atom.project.setPaths([subDir]) - - await atom.workspace.open('b.txt') - - const repo = atom.project.getRepositories()[0].async - - await repo.refreshStatus() - - const status = await repo.getCachedPathStatus(filePath) - expect(repo.isStatusModified(status)).toBe(false) - expect(repo.isStatusNew(status)).toBe(false) - }) - - it('caches the proper statuses when multiple project are open', async () => { - const otherWorkingDirectory = copyRepository() - - atom.project.setPaths([workingDirectory, otherWorkingDirectory]) - - await atom.workspace.open('b.txt') - - const repo = atom.project.getRepositories()[0].async - - await repo.refreshStatus() - - const subDir = path.join(workingDirectory, 'dir') - fs.mkdirSync(subDir) - - const filePath = path.join(subDir, 'b.txt') - fs.writeFileSync(filePath, 'some content!') - - const status = await repo.getCachedPathStatus(filePath) - expect(repo.isStatusModified(status)).toBe(true) - expect(repo.isStatusNew(status)).toBe(false) - }) - - it('emits did-change-statuses if the status changes', async () => { - const someNewPath = path.join(workingDirectory, 'MyNewJSFramework.md') - fs.writeFileSync(someNewPath, '') - - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatuses(statusHandler) - - await repo.refreshStatus() - - waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) - }) - - it('emits did-change-statuses if the branch changes', async () => { - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatuses(statusHandler) - - repo._refreshBranch = jasmine.createSpy('_refreshBranch').andCallFake(() => { - return Promise.resolve(true) - }) - - await repo.refreshStatus() - - waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) - }) - - it('emits did-change-statuses if the ahead/behind changes', async () => { - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatuses(statusHandler) - - repo._refreshAheadBehindCount = jasmine.createSpy('_refreshAheadBehindCount').andCallFake(() => { - return Promise.resolve(true) - }) - - await repo.refreshStatus() - - waitsFor('the onDidChangeStatuses handler to be called', () => statusHandler.callCount > 0) - }) - }) - - describe('.isProjectAtRoot()', () => { - it('returns true when the repository is at the root', async () => { - const workingDirectory = copyRepository() - atom.project.setPaths([workingDirectory]) - const repo = atom.project.getRepositories()[0].async - - const atRoot = await repo.isProjectAtRoot() - expect(atRoot).toBe(true) - }) - - it("returns false when the repository wasn't created with a project", async () => { - const workingDirectory = copyRepository() - const repo = GitRepositoryAsync.open(workingDirectory) - - const atRoot = await repo.isProjectAtRoot() - expect(atRoot).toBe(false) - }) - }) - - describe('buffer events', () => { - let repo - - beforeEach(() => { - const workingDirectory = copyRepository() - atom.project.setPaths([workingDirectory]) - - // When the path is added to the project, the repository is refreshed. We - // need to wait for that to complete before the tests continue so that - // we're in a known state. - repo = atom.project.getRepositories()[0].async - waitsForPromise(() => repo.refreshStatus()) - }) - - it('emits a status-changed event when a buffer is saved', async () => { - const editor = await atom.workspace.open('other.txt') - - editor.insertNewline() - - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatus(statusHandler) - editor.save() - - waitsFor('the onDidChangeStatus handler to be called', () => statusHandler.callCount > 0) - runs(() => { - expect(statusHandler.callCount).toBeGreaterThan(0) - expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256}) - }) - }) - - it('emits a status-changed event when a buffer is reloaded', async () => { - const editor = await atom.workspace.open('other.txt') - - fs.writeFileSync(editor.getPath(), 'changed') - - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatus(statusHandler) - editor.getBuffer().reload() - - waitsFor('the onDidChangeStatus handler to be called', () => statusHandler.callCount > 0) - runs(() => { - expect(statusHandler.callCount).toBeGreaterThan(0) - expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256}) - }) - }) - - it("emits a status-changed event when a buffer's path changes", async () => { - const editor = await atom.workspace.open('other.txt') - - fs.writeFileSync(editor.getPath(), 'changed') - - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatus(statusHandler) - editor.getBuffer().emitter.emit('did-change-path') - - waitsFor('the onDidChangeStatus handler to be called', () => statusHandler.callCount > 0) - runs(() => { - expect(statusHandler.callCount).toBeGreaterThan(0) - expect(statusHandler).toHaveBeenCalledWith({path: editor.getPath(), pathStatus: 256}) - - const pathHandler = jasmine.createSpy('pathHandler') - const buffer = editor.getBuffer() - buffer.onDidChangePath(pathHandler) - buffer.emitter.emit('did-change-path') - - waitsFor('the onDidChangePath handler to be called', () => pathHandler.callCount > 0) - runs(() => expect(pathHandler.callCount).toBeGreaterThan(0)) - }) - }) - - it('stops listening to the buffer when the repository is destroyed (regression)', async () => { - const editor = await atom.workspace.open('other.txt') - const repo = atom.project.getRepositories()[0] - repo.destroy() - expect(() => editor.save()).not.toThrow() - }) - }) - - describe('when a project is deserialized', () => { - let project2 - - beforeEach(() => { - atom.project.setPaths([copyRepository()]) - - // See the comment in the 'buffer events' beforeEach for why we need to do - // this. - const repository = atom.project.getRepositories()[0].async - waitsForPromise(() => repository.refreshStatus()) - }) - - afterEach(() => { - if (project2) project2.destroy() - }) - - it('subscribes to all the serialized buffers in the project', async () => { - await atom.workspace.open('file.txt') - - project2 = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm, applicationDelegate: atom.applicationDelegate}) - project2.deserialize(atom.project.serialize({isUnloading: true})) - - const repo = project2.getRepositories()[0].async - waitsForPromise(() => repo.refreshStatus()) - runs(() => { - const buffer = project2.getBuffers()[0] - - waitsFor(() => buffer.loaded) - runs(() => { - buffer.append('changes') - - const statusHandler = jasmine.createSpy('statusHandler') - repo.onDidChangeStatus(statusHandler) - buffer.save() - - waitsFor(() => statusHandler.callCount > 0) - runs(() => { - expect(statusHandler.callCount).toBeGreaterThan(0) - expect(statusHandler).toHaveBeenCalledWith({path: buffer.getPath(), pathStatus: 256}) - }) - }) - }) - }) - }) - - describe('GitRepositoryAsync::relativize(filePath, workdir)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - // This is a change in implementation from the git-utils version - it('just returns path if workdir is not provided', () => { - const _path = '/foo/bar/baz.txt' - const relPath = repo.relativize(_path) - expect(_path).toEqual(relPath) - }) - - it('relativizes a repo path', () => { - const workdir = '/tmp/foo/bar/baz/' - const relativizedPath = repo.relativize(`${workdir}a/b.txt`, workdir) - expect(relativizedPath).toBe('a/b.txt') - }) - - it("doesn't require workdir to end in a slash", () => { - const workdir = '/tmp/foo/bar/baz' - const relativizedPath = repo.relativize(`${workdir}/a/b.txt`, workdir) - expect(relativizedPath).toBe('a/b.txt') - }) - - it('preserves file case', () => { - repo.isCaseInsensitive = true - - const workdir = '/tmp/foo/bar/baz/' - const relativizedPath = repo.relativize(`${workdir}a/README.txt`, workdir) - expect(relativizedPath).toBe('a/README.txt') - }) - }) - - describe('.getShortHead(path)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns the human-readable branch name', async () => { - const head = await repo.getShortHead() - expect(head).toBe('master') - }) - - describe('in a submodule', () => { - beforeEach(() => { - const workingDirectory = copySubmoduleRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns the human-readable branch name', async () => { - await repo.refreshStatus() - - const head = await repo.getShortHead('jstips') - expect(head).toBe('test') - }) - }) - }) - - describe('.isSubmodule(path)', () => { - beforeEach(() => { - const workingDirectory = copySubmoduleRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it("returns false for a path that isn't a submodule", async () => { - const isSubmodule = await repo.isSubmodule('README') - expect(isSubmodule).toBe(false) - }) - - it('returns true for a path that is a submodule', async () => { - const isSubmodule = await repo.isSubmodule('jstips') - expect(isSubmodule).toBe(true) - }) - }) - - describe('.getAheadBehindCount(reference, path)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns 0, 0 for a branch with no upstream', async () => { - const {ahead, behind} = await repo.getAheadBehindCount('master') - expect(ahead).toBe(0) - expect(behind).toBe(0) - }) - }) - - describe('.getCachedUpstreamAheadBehindCount(path)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns 0, 0 for a branch with no upstream', async () => { - await repo.refreshStatus() - - const {ahead, behind} = await repo.getCachedUpstreamAheadBehindCount() - expect(ahead).toBe(0) - expect(behind).toBe(0) - }) - - describe('in a submodule', () => { - beforeEach(() => { - const workingDirectory = copySubmoduleRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns 1, 0 for a branch which is ahead by 1', async () => { - await repo.refreshStatus() - - const {ahead, behind} = await repo.getCachedUpstreamAheadBehindCount('You-Dont-Need-jQuery') - expect(ahead).toBe(1) - expect(behind).toBe(0) - }) - }) - }) - - describe('.getDiffStats(path)', () => { - let workingDirectory - beforeEach(() => { - workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns the diff stat', async () => { - const filePath = path.join(workingDirectory, 'a.txt') - fs.writeFileSync(filePath, 'change') - - const {added, deleted} = await repo.getDiffStats('a.txt') - expect(added).toBe(1) - expect(deleted).toBe(0) - }) - }) - - describe('.hasBranch(branch)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('resolves true when the branch exists', async () => { - const hasBranch = await repo.hasBranch('master') - expect(hasBranch).toBe(true) - }) - - it("resolves false when the branch doesn't exist", async () => { - const hasBranch = await repo.hasBranch('trolleybus') - expect(hasBranch).toBe(false) - }) - }) - - describe('.getReferences(path)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns the heads, remotes, and tags', async () => { - const {heads, remotes, tags} = await repo.getReferences() - expect(heads.length).toBe(1) - expect(remotes.length).toBe(0) - expect(tags.length).toBe(0) - }) - }) - - describe('.getReferenceTarget(reference, path)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns the SHA target', async () => { - const SHA = await repo.getReferenceTarget('refs/heads/master') - expect(SHA).toBe('8a9c86f1cb1f14b8f436eb91f4b052c8802ca99e') - }) - }) - - describe('.getConfigValue(key, path)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('looks up the value for the key', async () => { - const bare = await repo.getConfigValue('core.bare') - expect(bare).toBe('false') - }) - - it("resolves to null if there's no value", async () => { - const value = await repo.getConfigValue('my.special.key') - expect(value).toBeNull() - }) - }) - - describe('.checkoutReference(reference, create)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('can create new branches', () => { - let success = false - let threw = false - waitsForPromise(() => repo.checkoutReference('my-b', true) - .then(_ => success = true) - .catch(_ => threw = true)) - runs(() => { - expect(success).toBe(true) - expect(threw).toBe(false) - }) - }) - }) - - describe('.getLineDiffs(path, text)', () => { - beforeEach(() => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns the old and new lines of the diff', async () => { - const [{oldStart, newStart, oldLines, newLines}] = await repo.getLineDiffs('a.txt', 'hi there') - expect(oldStart).toBe(0) - expect(oldLines).toBe(0) - expect(newStart).toBe(1) - expect(newLines).toBe(1) - }) - }) - - describe('GitRepositoryAsync::relativizeToWorkingDirectory(_path)', () => { - let workingDirectory - - beforeEach(() => { - workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('relativizes the given path to the working directory of the repository', async () => { - let absolutePath = path.join(workingDirectory, 'a.txt') - expect(await repo.relativizeToWorkingDirectory(absolutePath)).toBe('a.txt') - absolutePath = path.join(workingDirectory, 'a/b/c.txt') - expect(await repo.relativizeToWorkingDirectory(absolutePath)).toBe('a/b/c.txt') - expect(await repo.relativizeToWorkingDirectory('a.txt')).toBe('a.txt') - expect(await repo.relativizeToWorkingDirectory('/not/in/workdir')).toBe('/not/in/workdir') - expect(await repo.relativizeToWorkingDirectory(null)).toBe(null) - expect(await repo.relativizeToWorkingDirectory()).toBe(undefined) - expect(await repo.relativizeToWorkingDirectory('')).toBe('') - expect(await repo.relativizeToWorkingDirectory(workingDirectory)).toBe('') - }) - - describe('when the opened path is a symlink', () => { - it('relativizes against both the linked path and real path', async () => { - // Symlinks require admin privs on windows so we just skip this there, - // done in git-utils as well - if (process.platform === 'win32') { - return - } - - const linkDirectory = path.join(temp.mkdirSync('atom-working-dir-symlink'), 'link') - fs.symlinkSync(workingDirectory, linkDirectory) - const linkedRepo = GitRepositoryAsync.open(linkDirectory) - expect(await linkedRepo.relativizeToWorkingDirectory(path.join(workingDirectory, 'test1'))).toBe('test1') - expect(await linkedRepo.relativizeToWorkingDirectory(path.join(linkDirectory, 'test2'))).toBe('test2') - expect(await linkedRepo.relativizeToWorkingDirectory(path.join(linkDirectory, 'test2/test3'))).toBe('test2/test3') - expect(await linkedRepo.relativizeToWorkingDirectory('test2/test3')).toBe('test2/test3') - }) - - it('handles case insensitive filesystems', async () => { - repo.isCaseInsensitive = true - expect(await repo.relativizeToWorkingDirectory(path.join(workingDirectory.toUpperCase(), 'a.txt'))).toBe('a.txt') - expect(await repo.relativizeToWorkingDirectory(path.join(workingDirectory.toUpperCase(), 'a/b/c.txt'))).toBe('a/b/c.txt') - }) - }) - }) - - describe('.getOriginURL()', () => { - beforeEach(() => { - const workingDirectory = copyRepository('repo-with-submodules') - repo = GitRepositoryAsync.open(workingDirectory) - }) - - it('returns the origin URL', async () => { - const url = await repo.getOriginURL() - expect(url).toBe('git@github.com:atom/some-repo-i-guess.git') - }) - }) - - describe('.getUpstreamBranch()', () => { - it('returns null when there is no upstream branch', async () => { - const workingDirectory = copyRepository() - repo = GitRepositoryAsync.open(workingDirectory) - - const upstream = await repo.getUpstreamBranch() - expect(upstream).toBe(null) - }) - - it('returns the upstream branch', async () => { - const workingDirectory = copyRepository('repo-with-submodules') - repo = GitRepositoryAsync.open(workingDirectory) - - const upstream = await repo.getUpstreamBranch() - expect(upstream).toBe('refs/remotes/origin/master') - }) - }) -}) diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index 21624c452..ef87d83ee 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -25,16 +25,6 @@ describe "GitRepository", -> it "returns null when no repository is found", -> expect(GitRepository.open(path.join(temp.dir, 'nogit.txt'))).toBeNull() - describe ".async", -> - it "returns a GitRepositoryAsync for the same repo", -> - repoPath = path.join(__dirname, 'fixtures', 'git', 'master.git') - repo = new GitRepository(repoPath) - onSuccess = jasmine.createSpy('onSuccess') - waitsForPromise -> - repo.async.getPath().then(onSuccess) - runs -> - expect(onSuccess.mostRecentCall.args[0]).toEqualPath(repoPath) - describe "new GitRepository(path)", -> it "throws an exception when no repository is found", -> expect(-> new GitRepository(path.join(temp.dir, 'nogit.txt'))).toThrow() diff --git a/src/git-repository-async.js b/src/git-repository-async.js deleted file mode 100644 index 66b73ba77..000000000 --- a/src/git-repository-async.js +++ /dev/null @@ -1,558 +0,0 @@ -'use babel' - -import {Repository} from 'ohnogit' -import {CompositeDisposable, Disposable} from 'event-kit' - -// For the most part, this class behaves the same as `GitRepository`, with a few -// notable differences: -// * Errors are generally propagated out to the caller instead of being -// swallowed within `GitRepositoryAsync`. -// * Methods accepting a path shouldn't be given a null path, unless it is -// specifically allowed as noted in the method's documentation. -export default class GitRepositoryAsync { - static open (path, options = {}) { - // QUESTION: Should this wrap Git.Repository and reject with a nicer message? - return new GitRepositoryAsync(path, options) - } - - static get Git () { - return Repository.Git - } - - // The name of the error thrown when an action is attempted on a destroyed - // repository. - static get DestroyedErrorName () { - return Repository.DestroyedErrorName - } - - constructor (_path, options = {}) { - this.repo = Repository.open(_path, options) - - this.subscriptions = new CompositeDisposable() - - let {refreshOnWindowFocus = true} = options - if (refreshOnWindowFocus) { - const onWindowFocus = () => this.refreshStatus() - window.addEventListener('focus', onWindowFocus) - this.subscriptions.add(new Disposable(() => window.removeEventListener('focus', onWindowFocus))) - } - - const {project, subscribeToBuffers} = options - this.project = project - if (this.project && subscribeToBuffers) { - this.project.getBuffers().forEach(buffer => this.subscribeToBuffer(buffer)) - this.subscriptions.add(this.project.onDidAddBuffer(buffer => this.subscribeToBuffer(buffer))) - } - } - - // This exists to provide backwards compatibility. - get _refreshingPromise () { - return this.repo._refreshingPromise - } - - get openedPath () { - return this.repo.openedPath - } - - // Public: Destroy this {GitRepositoryAsync} object. - // - // This destroys any tasks and subscriptions and releases the underlying - // libgit2 repository handle. This method is idempotent. - destroy () { - this.repo.destroy() - - if (this.subscriptions) { - this.subscriptions.dispose() - this.subscriptions = null - } - } - - // Event subscription - // ================== - - // Public: Invoke the given callback when this GitRepositoryAsync's destroy() - // method is invoked. - // - // * `callback` {Function} - // - // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidDestroy (callback) { - return this.repo.onDidDestroy(callback) - } - - // Public: Invoke the given callback when a specific file's status has - // changed. When a file is updated, reloaded, etc, and the status changes, this - // will be fired. - // - // * `callback` {Function} - // * `event` {Object} - // * `path` {String} the old parameters the decoration used to have - // * `pathStatus` {Number} representing the status. This value can be passed to - // {::isStatusModified} or {::isStatusNew} to get more information. - // - // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidChangeStatus (callback) { - return this.repo.onDidChangeStatus(callback) - } - - // Public: Invoke the given callback when a multiple files' statuses have - // changed. For example, on window focus, the status of all the paths in the - // repo is checked. If any of them have changed, this will be fired. Call - // {::getPathStatus(path)} to get the status for your path of choice. - // - // * `callback` {Function} - // - // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. - onDidChangeStatuses (callback) { - return this.repo.onDidChangeStatuses(callback) - } - - // Repository details - // ================== - - // Public: A {String} indicating the type of version control system used by - // this repository. - // - // Returns `"git"`. - getType () { - return 'git' - } - - // Public: Returns a {Promise} which resolves to the {String} path of the - // repository. - getPath () { - return this.repo.getPath() - } - - // Public: Returns a {Promise} which resolves to the {String} working - // directory path of the repository. - getWorkingDirectory (_path) { - return this.repo.getWorkingDirectory() - } - - // Public: Returns a {Promise} that resolves to true if at the root, false if - // in a subfolder of the repository. - isProjectAtRoot () { - if (!this.project) return Promise.resolve(false) - - if (!this.projectAtRoot) { - this.projectAtRoot = this.getWorkingDirectory() - .then(wd => this.project.relativize(wd) === '') - } - - return this.projectAtRoot - } - - // Public: Makes a path relative to the repository's working directory. - // - // * `path` The {String} path to relativize. - // - // Returns a {Promise} which resolves to the relative {String} path. - relativizeToWorkingDirectory (_path) { - return this.repo.relativizeToWorkingDirectory(_path) - } - - // Public: Makes a path relative to the repository's working directory. - // - // * `path` The {String} path to relativize. - // * `workingDirectory` The {String} working directory path. - // - // Returns the relative {String} path. - relativize (_path, workingDirectory) { - return this.repo.relativize(_path, workingDirectory) - } - - // Public: Returns a {Promise} which resolves to whether the given branch - // exists. - hasBranch (branch) { - return this.repo.hasBranch(branch) - } - - // Public: Retrieves a shortened version of the HEAD reference value. - // - // This removes the leading segments of `refs/heads`, `refs/tags`, or - // `refs/remotes`. It also shortens the SHA-1 of a detached `HEAD` to 7 - // characters. - // - // * `path` An optional {String} path in the repository to get this information - // for, only needed if the repository contains submodules. - // - // Returns a {Promise} which resolves to a {String}. - getShortHead (_path) { - return this.repo.getShortHead(_path) - } - - // Public: Is the given path a submodule in the repository? - // - // * `path` The {String} path to check. - // - // Returns a {Promise} that resolves true if the given path is a submodule in - // the repository. - isSubmodule (_path) { - return this.repo.isSubmodule(_path) - } - - // Public: Returns the number of commits behind the current branch is from the - // its upstream remote branch. - // - // * `reference` The {String} branch reference name. - // * `path` The {String} path in the repository to get this information - // for, only needed if the repository contains submodules. - // - // Returns a {Promise} which resolves to an {Object} with the following keys: - // * `ahead` The {Number} of commits ahead. - // * `behind` The {Number} of commits behind. - getAheadBehindCount (reference, _path) { - return this.repo.getAheadBehindCount(reference, _path) - } - - // Public: Get the cached ahead/behind commit counts for the current branch's - // upstream branch. - // - // * `path` An optional {String} path in the repository to get this information - // for, only needed if the repository has submodules. - // - // Returns a {Promise} which resolves to an {Object} with the following keys: - // * `ahead` The {Number} of commits ahead. - // * `behind` The {Number} of commits behind. - getCachedUpstreamAheadBehindCount (_path) { - return this.repo.getCachedUpstreamAheadBehindCount(_path) - } - - // Public: Returns the git configuration value specified by the key. - // - // * `path` An optional {String} path in the repository to get this information - // for, only needed if the repository has submodules. - // - // Returns a {Promise} which resolves to the {String} git configuration value - // specified by the key. - getConfigValue (key, _path) { - return this.repo.getConfigValue(key, _path) - } - - // Public: Get the URL for the 'origin' remote. - // - // * `path` (optional) {String} path in the repository to get this information - // for, only needed if the repository has submodules. - // - // Returns a {Promise} which resolves to the {String} origin url of the - // repository. - getOriginURL (_path) { - return this.repo.getOriginURL(_path) - } - - // Public: Returns the upstream branch for the current HEAD, or null if there - // is no upstream branch for the current HEAD. - // - // * `path` An optional {String} path in the repo to get this information for, - // only needed if the repository contains submodules. - // - // Returns a {Promise} which resolves to a {String} branch name such as - // `refs/remotes/origin/master`. - getUpstreamBranch (_path) { - return this.repo.getUpstreamBranch(_path) - } - - // Public: Gets all the local and remote references. - // - // * `path` An optional {String} path in the repository to get this information - // for, only needed if the repository has submodules. - // - // Returns a {Promise} which resolves to an {Object} with the following keys: - // * `heads` An {Array} of head reference names. - // * `remotes` An {Array} of remote reference names. - // * `tags` An {Array} of tag reference names. - getReferences (_path) { - return this.repo.getReferences(_path) - } - - // Public: Get the SHA for the given reference. - // - // * `reference` The {String} reference to get the target of. - // * `path` An optional {String} path in the repo to get the reference target - // for. Only needed if the repository contains submodules. - // - // Returns a {Promise} which resolves to the current {String} SHA for the - // given reference. - getReferenceTarget (reference, _path) { - return this.repo.getReferenceTarget(reference, _path) - } - - // Reading Status - // ============== - - // Public: Resolves true if the given path is modified. - // - // * `path` The {String} path to check. - // - // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` - // is modified. - isPathModified (_path) { - return this.repo.isPathModified(_path) - } - - // Public: Resolves true if the given path is new. - // - // * `path` The {String} path to check. - // - // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` - // is new. - isPathNew (_path) { - return this.repo.isPathNew(_path) - } - - // Public: Is the given path ignored? - // - // * `path` The {String} path to check. - // - // Returns a {Promise} which resolves to a {Boolean} that's true if the `path` - // is ignored. - isPathIgnored (_path) { - return this.repo.isPathIgnored(_path) - } - - // Get the status of a directory in the repository's working directory. - // - // * `directoryPath` The {String} path to check. - // - // Returns a {Promise} resolving to a {Number} representing the status. This - // value can be passed to {::isStatusModified} or {::isStatusNew} to get more - // information. - getDirectoryStatus (directoryPath) { - return this.repo.getDirectoryStatus(directoryPath) - } - - // Refresh the status bit for the given path. - // - // Note that if the status of the path has changed, this will emit a - // 'did-change-status' event. - // - // * `path` The {String} path whose status should be refreshed. - // - // Returns a {Promise} which resolves to a {Number} which is the refreshed - // status bit for the path. - refreshStatusForPath (_path) { - return this.repo.refreshStatusForPath(_path) - } - - // Returns a Promise that resolves to the status bit of a given path if it has - // one, otherwise 'current'. - getPathStatus (_path) { - return this.refreshStatusForPath(_path) - } - - // Public: Get the cached status for the given path. - // - // * `path` A {String} path in the repository, relative or absolute. - // - // Returns a {Promise} which resolves to a status {Number} or null if the - // path is not in the cache. - getCachedPathStatus (_path) { - return this.repo.getCachedPathStatus(_path) - } - - // Public: Get the cached statuses for the repository. - // - // Returns an {Object} of {Number} statuses, keyed by {String} working - // directory-relative file names. - getCachedPathStatuses () { - return this.repo.pathStatusCache - } - - // Public: Returns true if the given status indicates modification. - // - // * `statusBit` A {Number} representing the status. - // - // Returns a {Boolean} that's true if the `statusBit` indicates modification. - isStatusModified (statusBit) { - return this.repo.isStatusModified(statusBit) - } - - // Public: Returns true if the given status indicates a new path. - // - // * `statusBit` A {Number} representing the status. - // - // Returns a {Boolean} that's true if the `statusBit` indicates a new path. - isStatusNew (statusBit) { - return this.repo.isStatusNew(statusBit) - } - - // Public: Returns true if the given status indicates the path is staged. - // - // * `statusBit` A {Number} representing the status. - // - // Returns a {Boolean} that's true if the `statusBit` indicates the path is - // staged. - isStatusStaged (statusBit) { - return this.repo.isStatusStaged(statusBit) - } - - // Public: Returns true if the given status indicates the path is ignored. - // - // * `statusBit` A {Number} representing the status. - // - // Returns a {Boolean} that's true if the `statusBit` indicates the path is - // ignored. - isStatusIgnored (statusBit) { - return this.repo.isStatusIgnored(statusBit) - } - - // Public: Returns true if the given status indicates the path is deleted. - // - // * `statusBit` A {Number} representing the status. - // - // Returns a {Boolean} that's true if the `statusBit` indicates the path is - // deleted. - isStatusDeleted (statusBit) { - return this.repo.isStatusDeleted(statusBit) - } - - // Retrieving Diffs - // ================ - // Public: Retrieves the number of lines added and removed to a path. - // - // This compares the working directory contents of the path to the `HEAD` - // version. - // - // * `path` The {String} path to check. - // - // Returns a {Promise} which resolves to an {Object} with the following keys: - // * `added` The {Number} of added lines. - // * `deleted` The {Number} of deleted lines. - getDiffStats (_path) { - return this.repo.getDiffStats(_path) - } - - // Public: Retrieves the line diffs comparing the `HEAD` version of the given - // path and the given text. - // - // * `path` The {String} path relative to the repository. - // * `text` The {String} to compare against the `HEAD` contents - // - // Returns an {Array} of hunk {Object}s with the following keys: - // * `oldStart` The line {Number} of the old hunk. - // * `newStart` The line {Number} of the new hunk. - // * `oldLines` The {Number} of lines in the old hunk. - // * `newLines` The {Number} of lines in the new hunk - getLineDiffs (_path, text) { - return this.repo.getLineDiffs(_path, text) - } - - // Checking Out - // ============ - - // Public: Restore the contents of a path in the working directory and index - // to the version at `HEAD`. - // - // This is essentially the same as running: - // - // ```sh - // git reset HEAD -- - // git checkout HEAD -- - // ``` - // - // * `path` The {String} path to checkout. - // - // Returns a {Promise} that resolves or rejects depending on whether the - // method was successful. - checkoutHead (_path) { - return this.repo.checkoutHead(_path) - } - - // Public: Checks out a branch in your repository. - // - // * `reference` The {String} reference to checkout. - // * `create` A {Boolean} value which, if true creates the new reference if - // it doesn't exist. - // - // Returns a {Promise} that resolves if the method was successful. - checkoutReference (reference, create) { - return this.repo.checkoutReference(reference, create) - } - - // Private - // ======= - - checkoutHeadForEditor (editor) { - const filePath = editor.getPath() - if (!filePath) { - return Promise.reject() - } - - if (editor.buffer.isModified()) { - editor.buffer.reload() - } - - return this.checkoutHead(filePath) - } - - // Refreshes the git status. - // - // Returns a {Promise} which will resolve to {null} when refresh is complete. - refreshStatus () { - let projectPathsPromises = [Promise.resolve('')] - if (this.project) { - projectPathsPromises = this.project.getPaths() - .map(p => this.relativizeToWorkingDirectory(p)) - } - - return Promise.all(projectPathsPromises) - .then(paths => paths.map(p => p.length > 0 ? p + '/**' : '*')) - .then(pathspecs => this.repo.refreshStatus(pathspecs)) - } - - // Get the NodeGit repository for the given path. - // - // * `path` The optional {String} path within the repository. This is only - // needed if you want to get the repository for that path if it is a - // submodule. - // - // Returns a {Promise} which resolves to the {NodeGit.Repository}. - getRepo (_path) { - return this.repo.getRepo(_path) - } - - // Open a new instance of the underlying {NodeGit.Repository}. - // - // By opening multiple connections to the same underlying repository, users - // can safely access the same repository concurrently. - // - // Returns the new {NodeGit.Repository}. - openRepository () { - return this.repo.openRepository() - } - - // Section: Private - // ================ - - // Has the repository been destroyed? - // - // Returns a {Boolean}. - _isDestroyed () { - return this.repo._isDestroyed() - } - - // Subscribe to events on the given buffer. - subscribeToBuffer (buffer) { - const bufferSubscriptions = new CompositeDisposable() - - const refreshStatusForBuffer = () => { - const _path = buffer.getPath() - if (_path) { - this.refreshStatusForPath(_path) - } - } - - bufferSubscriptions.add( - buffer.onDidSave(refreshStatusForBuffer), - buffer.onDidReload(refreshStatusForBuffer), - buffer.onDidChangePath(refreshStatusForBuffer), - buffer.onDidDestroy(() => { - bufferSubscriptions.dispose() - this.subscriptions.remove(bufferSubscriptions) - }) - ) - - this.subscriptions.add(bufferSubscriptions) - } -} diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 6f0851cf9..3a2477bc8 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -3,7 +3,6 @@ _ = require 'underscore-plus' {Emitter, Disposable, CompositeDisposable} = require 'event-kit' fs = require 'fs-plus' -GitRepositoryAsync = require './git-repository-async' GitUtils = require 'git-utils' Task = require './task' @@ -76,13 +75,6 @@ class GitRepository unless @repo? throw new Error("No Git repository found searching path: #{path}") - asyncOptions = _.clone(options) - # GitRepository itself will handle these cases by manually calling through - # to the async repo. - asyncOptions.refreshOnWindowFocus = false - asyncOptions.subscribeToBuffers = false - @async = GitRepositoryAsync.open(path, asyncOptions) - @statuses = {} @upstream = {ahead: 0, behind: 0} for submodulePath, submoduleRepo of @repo.submodules From a973e1ae9e6c3318ddfd0fe3c144e1d275244d38 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Jul 2016 14:04:58 -0700 Subject: [PATCH 17/21] Put back GitRepository::isDestroyed() method --- src/git-repository.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 3a2477bc8..85600bba7 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -117,6 +117,10 @@ class GitRepository @subscriptions.dispose() @subscriptions = null + # Public: Returns a {Boolean} indicating if this repository has been destroyed. + isDestroyed: -> + not @repo? + # Public: Invoke the given callback when this GitRepository's destroy() method # is invoked. # From 47df8c5d77d86dcc5038a6121ed500e4a51ec6af Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Jul 2016 14:37:37 -0700 Subject: [PATCH 18/21] Upgrade packages to use sync git repository again Signed-off-by: Nathan Sobo --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 186c22ecc..1829a077b 100644 --- a/package.json +++ b/package.json @@ -93,8 +93,8 @@ "encoding-selector": "0.22.0", "exception-reporting": "0.38.1", "find-and-replace": "0.201.0", - "fuzzy-finder": "1.2.0", - "git-diff": "1.0.1", + "fuzzy-finder": "1.3.0-sync-git", + "git-diff": "1.1.0-sync-git", "go-to-line": "0.31.0", "grammar-selector": "0.48.1", "image-view": "0.58.2", @@ -105,12 +105,12 @@ "markdown-preview": "0.158.0", "metrics": "0.53.1", "notifications": "0.65.0", - "open-on-github": "1.1.0", + "open-on-github": "1.2.0-sync-git", "package-generator": "1.0.0", "settings-view": "0.240.1", "snippets": "1.0.2", "spell-check": "0.67.1", - "status-bar": "1.3.1", + "status-bar": "1.4.0-sync-git", "styleguide": "0.47.0", "symbols-view": "0.113.0", "tabs": "0.99.0", From 678d5b4c247ae63cce2bc73a383cb6a493d4eb89 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 13 Jul 2016 15:41:10 -0700 Subject: [PATCH 19/21] Remove usage of repository.async in Workspace Signed-off-by: Nathan Sobo --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index bde2a62d3..8d0ff38fd 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -1097,7 +1097,7 @@ class Workspace extends Model checkoutHead = => @project.repositoryForDirectory(new Directory(editor.getDirectoryPath())) .then (repository) -> - repository?.async.checkoutHeadForEditor(editor) + repository?.checkoutHeadForEditor(editor) if @config.get('editor.confirmCheckoutHeadRevision') @applicationDelegate.confirm From 15cf9fb0d07e104de6e84b8cadb5fdd30fd27ab0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 14 Jul 2016 11:03:45 -0700 Subject: [PATCH 20/21] Upgrade to minor releases of packages --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 1829a077b..4083f8a83 100644 --- a/package.json +++ b/package.json @@ -93,8 +93,8 @@ "encoding-selector": "0.22.0", "exception-reporting": "0.38.1", "find-and-replace": "0.201.0", - "fuzzy-finder": "1.3.0-sync-git", - "git-diff": "1.1.0-sync-git", + "fuzzy-finder": "1.3.0", + "git-diff": "1.1.0", "go-to-line": "0.31.0", "grammar-selector": "0.48.1", "image-view": "0.58.2", @@ -105,12 +105,12 @@ "markdown-preview": "0.158.0", "metrics": "0.53.1", "notifications": "0.65.0", - "open-on-github": "1.2.0-sync-git", + "open-on-github": "1.2.0", "package-generator": "1.0.0", "settings-view": "0.240.1", "snippets": "1.0.2", "spell-check": "0.67.1", - "status-bar": "1.4.0-sync-git", + "status-bar": "1.4.0", "styleguide": "0.47.0", "symbols-view": "0.113.0", "tabs": "0.99.0", From f83ebf337caf0e48c0268387208921c23840570e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 14 Jul 2016 12:58:25 -0700 Subject: [PATCH 21/21] Clear out apm folder in cibuild --- script/cibuild | 1 + 1 file changed, 1 insertion(+) diff --git a/script/cibuild b/script/cibuild index 860e0a938..627c2291a 100755 --- a/script/cibuild +++ b/script/cibuild @@ -62,6 +62,7 @@ function removeNodeModules() { try { fsPlus.removeSync(path.resolve(__dirname, '..', 'node_modules')); + fsPlus.removeSync(path.resolve(__dirname, '..', 'apm', 'node_modules')); } catch (error) { console.error(error.message); process.exit(1);