From c19ed790665ad123d6fb52e79051a68534befe65 Mon Sep 17 00:00:00 2001 From: Ross Allen Date: Wed, 8 Jun 2016 16:36:37 -0700 Subject: [PATCH 01/32] 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/32] 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/32] 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 03257b17e23b55315c5c66bcfed91aa567eb8ee8 Mon Sep 17 00:00:00 2001 From: Jonah Stiennon Date: Wed, 22 Jun 2016 15:38:51 -0700 Subject: [PATCH 04/32] :memo: Mention how models must implement getTitle() [ci skip] --- src/view-registry.coffee | 3 +++ src/workspace.coffee | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/view-registry.coffee b/src/view-registry.coffee index 5fbfba729..f5f8651df 100644 --- a/src/view-registry.coffee +++ b/src/view-registry.coffee @@ -15,6 +15,9 @@ AnyConstructor = Symbol('any-constructor') # application logic and is the primary point of API interaction. The view # just handles presentation. # +# Note: Models can be any object, but must implement a `getTitle()` function +# if they are to be displayed in a {Pane} +# # View providers inform the workspace how your model objects should be # presented in the DOM. A view provider must always return a DOM node, which # makes [HTML 5 custom elements](http://www.html5rocks.com/en/tutorials/webcomponents/customelements/) diff --git a/src/workspace.coffee b/src/workspace.coffee index c2ed18705..d64898169 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -589,7 +589,10 @@ class Workspace extends Model # Public: Register an opener for a uri. # - # An {TextEditor} will be used if no openers return a value. + # Atom loops through opener functions until one returns a value for a given uri. + # Openers are expected to return an object that inherits from HTMLElement or + # a model which has an associated view in the {ViewRegistry}. + # A {TextEditor} will be used if no openers return a value. # # ## Examples # From 76e59a25f4657ff80553324ff3aa8e13f24e31a9 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Wed, 22 Jun 2016 14:32:40 -0700 Subject: [PATCH 05/32] Don't erase NODE_ENV from environment. Atom sets this to 'production' earlier, but some code paths can cause it to be un-set. This degrades performance and sometimes crashes React. Fixes #12024. --- src/environment-helpers.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/environment-helpers.js b/src/environment-helpers.js index 5a9ef8e3a..f4224fbe4 100644 --- a/src/environment-helpers.js +++ b/src/environment-helpers.js @@ -72,7 +72,10 @@ function needsPatching (options = { platform: process.platform, env: process.env // underlying functionality. function clone (to, from) { for (var key in to) { - delete to[key] + // Don't erase NODE_ENV. Fixes #12024 + if (key !== 'NODE_ENV') { + delete to[key] + } } Object.assign(to, from) From 341a9602b293b4fdcf69f62ced2c50fc6f615eae Mon Sep 17 00:00:00 2001 From: Damien Guard Date: Mon, 27 Jun 2016 22:34:37 -0700 Subject: [PATCH 06/32] Use in-path 7z on AppVeyor, tidy-up publish --- build/tasks/publish-build-task.coffee | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index 0a18c9c23..2802f827d 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -11,6 +11,7 @@ AWS = require 'aws-sdk' grunt = null token = process.env.ATOM_ACCESS_TOKEN +repo = process.env.ATOM_REPO ? 'atom/atom' defaultHeaders = Authorization: "token #{token}" 'User-Agent': 'Atom' @@ -119,7 +120,8 @@ logError = (message, error, details) -> zipAssets = (buildDir, assets, callback) -> zip = (directory, sourcePath, assetName, callback) -> if process.platform is 'win32' - zipCommand = "C:/psmodules/7z.exe a -r #{assetName} \"#{sourcePath}\"" + sevenZipPath = if process.env.JANKY_SHA1? then "C:/psmodules/" else "" + zipCommand = "#{sevenZipPath}7z.exe a -r \"#{assetName}\" \"#{sourcePath}\"" else zipCommand = "zip -r --symlinks '#{assetName}' '#{sourcePath}'" options = {cwd: directory, maxBuffer: Infinity} @@ -134,10 +136,10 @@ zipAssets = (buildDir, assets, callback) -> async.parallel(tasks, callback) getAtomDraftRelease = (isPrerelease, branchName, callback) -> - atomRepo = new GitHub({repo: 'atom/atom', token}) + atomRepo = new GitHub({repo: repo, token}) atomRepo.getReleases {prerelease: isPrerelease}, (error, releases=[]) -> if error? - logError('Fetching atom/atom releases failed', error, releases) + logError("Fetching #{repo} #{if isPrerelease then "pre" else "" }releases failed", error, releases) callback(error) else [firstDraft] = releases.filter ({draft}) -> draft @@ -160,7 +162,7 @@ getAtomDraftRelease = (isPrerelease, branchName, callback) -> createAtomDraftRelease = (isPrerelease, branchName, callback) -> {version} = require('../../package.json') options = - uri: 'https://api.github.com/repos/atom/atom/releases' + uri: "https://api.github.com/repos/#{repo}/releases" method: 'POST' headers: defaultHeaders json: @@ -177,7 +179,7 @@ createAtomDraftRelease = (isPrerelease, branchName, callback) -> request options, (error, response, body='') -> if error? or response.statusCode isnt 201 - logError("Creating atom/atom draft release failed", error, body) + logError("Creating #{repo} draft release failed", error, body) callback(error ? new Error(response.statusCode)) else callback(null, body) From 7b16e40fa8e8bd4a7ab2a1ffd6e5dc1e781fac55 Mon Sep 17 00:00:00 2001 From: Jonah Date: Tue, 28 Jun 2016 17:52:53 -0700 Subject: [PATCH 07/32] :memo: better docs for workspace.addOpener --- src/workspace.coffee | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index d64898169..bde2a62d3 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -589,10 +589,11 @@ class Workspace extends Model # Public: Register an opener for a uri. # - # Atom loops through opener functions until one returns a value for a given uri. + # When a URI is opened via {Workspace::open}, Atom loops through its registered + # opener functions until one returns a value for the given uri. # Openers are expected to return an object that inherits from HTMLElement or # a model which has an associated view in the {ViewRegistry}. - # A {TextEditor} will be used if no openers return a value. + # A {TextEditor} will be used if no opener returns a value. # # ## Examples # From eb0cc530fd3fcc16fe5e74bdba8f449120193d57 Mon Sep 17 00:00:00 2001 From: simurai Date: Wed, 29 Jun 2016 13:15:20 +0900 Subject: [PATCH 08/32] :fire: Remove -webkit prefixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit that aren’t needed anymore. --- static/linux.less | 2 +- static/panes.less | 22 +++++++++++----------- static/syntax.less | 8 ++++---- static/text-editor-light.less | 6 +++--- static/text-editor-shadow.less | 4 ++-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/static/linux.less b/static/linux.less index 183353821..7a48e7fcf 100644 --- a/static/linux.less +++ b/static/linux.less @@ -8,6 +8,6 @@ } ::-webkit-scrollbar-thumb { - -webkit-border-radius: 2px; + border-radius: 2px; background: rgba(150, 150, 150, .33); } diff --git a/static/panes.less b/static/panes.less index 418027772..a49e11fd6 100644 --- a/static/panes.less +++ b/static/panes.less @@ -5,13 +5,13 @@ // editor resource with a tab. atom-pane-container { position: relative; - display: -webkit-flex; - -webkit-flex: 1; + display: flex; + flex: 1; min-width: 0; atom-pane-axis { - display: -webkit-flex; - -webkit-flex: 1; + display: flex; + flex: 1; min-width: 0; & > atom-pane-resize-handle { @@ -21,7 +21,7 @@ atom-pane-container { } atom-pane-axis.vertical { - -webkit-flex-direction: column; + flex-direction: column; & > atom-pane-resize-handle { width: 100%; @@ -33,7 +33,7 @@ atom-pane-container { } atom-pane-axis.horizontal { - -webkit-flex-direction: row; + flex-direction: row; & > atom-pane-resize-handle { width: 8px; @@ -46,15 +46,15 @@ atom-pane-container { atom-pane { position: relative; - display: -webkit-flex; - -webkit-flex: 1; - -webkit-flex-direction: column; + display: flex; + flex: 1; + flex-direction: column; overflow: visible; min-width: 0; .item-views { - -webkit-flex: 1; - display: -webkit-flex; + flex: 1; + display: flex; min-height: 0; min-width: 0; position: relative; diff --git a/static/syntax.less b/static/syntax.less index 657947793..601d09fd7 100644 --- a/static/syntax.less +++ b/static/syntax.less @@ -23,13 +23,13 @@ atom-text-editor { .define-selection-flash-color-if-not-defined() { @syntax-selection-flash-color: rgba(100, 255, 100, 0.7); } .define-selection-flash-color-if-not-defined(); -@-webkit-keyframes flash { +@keyframes flash { from { background-color: @syntax-selection-flash-color; } to { background-color: null; } } atom-text-editor .flash.selection .region { - -webkit-animation-name: flash; - -webkit-animation-duration: .5s; - -webkit-animation-iteration-count: 1; + animation-name: flash; + animation-duration: .5s; + animation-iteration-count: 1; } diff --git a/static/text-editor-light.less b/static/text-editor-light.less index f5429fd7f..bc699e698 100644 --- a/static/text-editor-light.less +++ b/static/text-editor-light.less @@ -22,13 +22,13 @@ atom-overlay { // TODO: Remove the following styles when the editor shadow DOM can no longer be disabled atom-text-editor { - display: -webkit-flex; + display: flex; .editor-contents { width: 100%; overflow: hidden; cursor: text; - display: -webkit-flex; + display: flex; -webkit-user-select: none; position: relative; } @@ -96,7 +96,7 @@ atom-text-editor { z-index: 0; overflow: hidden; - -webkit-flex: 1; + flex: 1; min-width: 0; } diff --git a/static/text-editor-shadow.less b/static/text-editor-shadow.less index e481d11b8..f49377ad6 100644 --- a/static/text-editor-shadow.less +++ b/static/text-editor-shadow.less @@ -10,7 +10,7 @@ .editor-contents--private { width: 100%; cursor: text; - display: -webkit-flex; + display: flex; -webkit-user-select: none; position: relative; } @@ -79,7 +79,7 @@ z-index: 0; overflow: hidden; - -webkit-flex: 1; + flex: 1; min-width: 0; } From b9515ea4772c1c92332bb9ec881b8c19bb0f31a3 Mon Sep 17 00:00:00 2001 From: simurai Date: Wed, 29 Jun 2016 13:20:02 +0900 Subject: [PATCH 09/32] Add min-width to atom-workspace-axis.horizontal This is just a precaution against https://github.com/atom/atom/pull/11866 --- static/workspace-view.less | 1 + 1 file changed, 1 insertion(+) diff --git a/static/workspace-view.less b/static/workspace-view.less index 372c89814..6d272a018 100644 --- a/static/workspace-view.less +++ b/static/workspace-view.less @@ -17,6 +17,7 @@ atom-workspace { atom-workspace-axis.horizontal { display: flex; flex: 1; + min-width: 0; } atom-workspace-axis.vertical { From 9ea68024acccd7dc7494f50d03496c16b193c0c4 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Wed, 29 Jun 2016 15:23:44 -0700 Subject: [PATCH 10/32] :arrow_up: settings-view@0.239.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0c1b3ceb..5fcc8de0e 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.238.1", + "settings-view": "0.239.0", "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.3.1", From 7e75b861d8b9b26382764bea587b676a53c7d8a1 Mon Sep 17 00:00:00 2001 From: Ross Allen Date: Thu, 30 Jun 2016 15:57:22 -0700 Subject: [PATCH 11/32] 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 afad72216a9d5d5f5d8f25bca8d3e7840b47d0aa Mon Sep 17 00:00:00 2001 From: simurai Date: Sat, 2 Jul 2016 16:55:43 +0900 Subject: [PATCH 12/32] :arrow_up: about@v1.5.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5fcc8de0e..4b9b0f5ba 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "one-light-syntax": "1.3.0", "solarized-dark-syntax": "1.0.2", "solarized-light-syntax": "1.0.2", - "about": "1.5.2", + "about": "1.5.3", "archive-view": "0.61.1", "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.2", From a08a8ea66416ae1d626238828c0f509ff16de263 Mon Sep 17 00:00:00 2001 From: simurai Date: Sat, 2 Jul 2016 17:02:45 +0900 Subject: [PATCH 13/32] :arrow_up: settings-view@v0.240.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b9b0f5ba..4b4fca29f 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.239.0", + "settings-view": "0.240.0", "snippets": "1.0.2", "spell-check": "0.67.1", "status-bar": "1.3.1", From e81c805e05837e83fb49fc882dd200e9a02510b6 Mon Sep 17 00:00:00 2001 From: simurai Date: Sat, 2 Jul 2016 17:37:00 +0900 Subject: [PATCH 14/32] :arrow_up: autocomplete-plus@v2.31.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b4fca29f..7aa699a51 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "autocomplete-atom-api": "0.10.0", "autocomplete-css": "0.11.2", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.31.0", + "autocomplete-plus": "2.31.1", "autocomplete-snippets": "1.11.0", "autoflow": "0.27.0", "autosave": "0.23.1", From 7f61f4388e621a4e041e500911fb69723729ce14 Mon Sep 17 00:00:00 2001 From: Thomas Johansen Date: Mon, 4 Jul 2016 06:34:11 +0200 Subject: [PATCH 15/32] :arrow_up: image-view@0.58.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7aa699a51..3c7ccbd19 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "git-diff": "1.0.1", "go-to-line": "0.31.0", "grammar-selector": "0.48.1", - "image-view": "0.58.1", + "image-view": "0.58.2", "incompatible-packages": "0.26.1", "keybinding-resolver": "0.35.0", "line-ending-selector": "0.5.0", From 6f78d5c0f9dc1a2a24698840eff2a81b7d9e0669 Mon Sep 17 00:00:00 2001 From: Ammar Najjar Date: Mon, 4 Jul 2016 10:47:31 +0200 Subject: [PATCH 16/32] update nodejs installation instructions link for Fedora/Centos/RHEL --- docs/build-instructions/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/build-instructions/linux.md b/docs/build-instructions/linux.md index 126604c49..f737a6cda 100644 --- a/docs/build-instructions/linux.md +++ b/docs/build-instructions/linux.md @@ -25,7 +25,7 @@ Ubuntu LTS 12.04 64-bit is the recommended platform. ### Fedora / CentOS / RHEL * `sudo dnf --assumeyes install make gcc gcc-c++ glibc-devel git-core libgnome-keyring-devel rpmdevtools` -* Instructions for [Node.js](https://github.com/nodejs/node-v0.x-archive/wiki/Installing-Node.js-via-package-manager#enterprise-linux-and-fedora). +* Instructions for [Node.js](https://nodejs.org/en/download/package-manager/#enterprise-linux-and-fedora). ### Arch From 4986a64815375e9abe1f080d3f96a6b290b3d222 Mon Sep 17 00:00:00 2001 From: Glavin Wiechert Date: Sat, 2 Jul 2016 16:26:36 -0300 Subject: [PATCH 17/32] 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 98fc8db3daa2021d391f03f93b1de8f40e424cb4 Mon Sep 17 00:00:00 2001 From: Lee Dohm Date: Tue, 5 Jul 2016 10:23:02 -0700 Subject: [PATCH 18/32] :arrow_up: find-and-replace@0.201.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3c7ccbd19..29152f4ad 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "dev-live-reload": "0.47.0", "encoding-selector": "0.22.0", "exception-reporting": "0.38.1", - "find-and-replace": "0.200.0", + "find-and-replace": "0.201.0", "fuzzy-finder": "1.2.0", "git-diff": "1.0.1", "go-to-line": "0.31.0", From 9bf329cef250188c7917ce9d00ebf5bd9db7996f Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Thu, 7 Jul 2016 15:53:07 -0700 Subject: [PATCH 19/32] 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 20/32] :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 21/32] :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 22/32] 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 23/32] :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 24/32] :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 25/32] 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 26/32] :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 27/32] :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 28/32] 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 29/32] 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 30/32] 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 31/32] 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 32/32] 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