{"action":"created","comment":{"url":"https://api.github.com/repos/atom/atom/pulls/comments/47299814","id":47299814,"diff_hunk":"@@ -0,0 +1,871 @@\n+'use babel'\n+\n+import fs from 'fs-plus'\n+import Git from 'nodegit'\n+import path from 'path'\n+import {Emitter, CompositeDisposable, Disposable} from 'event-kit'\n+\n+const modifiedStatusFlags = Git.Status.STATUS.WT_MODIFIED | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.WT_TYPECHANGE | Git.Status.STATUS.INDEX_TYPECHANGE\n+const newStatusFlags = Git.Status.STATUS.WT_NEW | Git.Status.STATUS.INDEX_NEW\n+const deletedStatusFlags = Git.Status.STATUS.WT_DELETED | Git.Status.STATUS.INDEX_DELETED\n+const indexStatusFlags = Git.Status.STATUS.INDEX_NEW | Git.Status.STATUS.INDEX_MODIFIED | Git.Status.STATUS.INDEX_DELETED | Git.Status.STATUS.INDEX_RENAMED | Git.Status.STATUS.INDEX_TYPECHANGE\n+const submoduleMode = 57344 // TODO compose this from libgit2 constants\n+\n+// Just using this for _.isEqual and _.object, we should impl our own here\n+import _ from 'underscore-plus'\n+\n+export default class GitRepositoryAsync {\n+  static open (path, options = {}) {\n+    // QUESTION: Should this wrap Git.Repository and reject with a nicer message?\n+    return new GitRepositoryAsync(path, options)\n+  }\n+\n+  static get Git () {\n+    return Git\n+  }\n+\n+  static get DestroyedErrorName () {\n+    return 'GitRepositoryAsync.destroyed'\n+  }\n+\n+  constructor (_path, options) {\n+    this.repo = null\n+    this.emitter = new Emitter()\n+    this.subscriptions = new CompositeDisposable()\n+    this.pathStatusCache = {}\n+    this.repoPromise = Git.Repository.open(_path)\n+    this.isCaseInsensitive = fs.isCaseInsensitive()\n+    this.upstreamByPath = {}\n+\n+    this._refreshingCount = 0\n+\n+    options = options || {}\n+\n+    let {refreshOnWindowFocus = true} = options\n+    if (refreshOnWindowFocus) {\n+      const onWindowFocus = () => this.refreshStatus()\n+      window.addEventListener('focus', onWindowFocus)\n+      this.subscriptions.add(new Disposable(() => window.removeEventListener('focus', onWindowFocus)))\n+    }\n+\n+    const {project, subscribeToBuffers} = options\n+    this.project = project\n+    if (this.project && subscribeToBuffers) {\n+      this.project.getBuffers().forEach(buffer => this.subscribeToBuffer(buffer))\n+      this.subscriptions.add(this.project.onDidAddBuffer(buffer => this.subscribeToBuffer(buffer)))\n+    }\n+  }\n+\n+  // Public: Destroy this {GitRepositoryAsync} object.\n+  //\n+  // This destroys any tasks and subscriptions and releases the underlying\n+  // libgit2 repository handle. This method is idempotent.\n+  destroy () {\n+    if (this.emitter) {\n+      this.emitter.emit('did-destroy')\n+      this.emitter.dispose()\n+      this.emitter = null\n+    }\n+    if (this.subscriptions) {\n+      this.subscriptions.dispose()\n+      this.subscriptions = null\n+    }\n+\n+    this.repoPromise = null\n+  }\n+\n+  // Event subscription\n+  // ==================\n+\n+  // Public: Invoke the given callback when this GitRepositoryAsync's destroy()\n+  // method is invoked.\n+  //\n+  // * `callback` {Function}\n+  //\n+  // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.\n+  onDidDestroy (callback) {\n+    return this.emitter.on('did-destroy', callback)\n+  }\n+\n+  // Public: Invoke the given callback when a specific file's status has\n+  // changed. When a file is updated, reloaded, etc, and the status changes, this\n+  // will be fired.\n+  //\n+  // * `callback` {Function}\n+  //   * `event` {Object}\n+  //     * `path` {String} the old parameters the decoration used to have\n+  //     * `pathStatus` {Number} representing the status. This value can be passed to\n+  //       {::isStatusModified} or {::isStatusNew} to get more information.\n+  //\n+  // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.\n+  onDidChangeStatus (callback) {\n+    return this.emitter.on('did-change-status', callback)\n+  }\n+\n+  // Public: Invoke the given callback when a multiple files' statuses have\n+  // changed. For example, on window focus, the status of all the paths in the\n+  // repo is checked. If any of them have changed, this will be fired. Call\n+  // {::getPathStatus(path)} to get the status for your path of choice.\n+  //\n+  // * `callback` {Function}\n+  //\n+  // Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.\n+  onDidChangeStatuses (callback) {\n+    return this.emitter.on('did-change-statuses', callback)\n+  }\n+\n+  // Repository details\n+  // ==================\n+\n+  // Public: A {String} indicating the type of version control system used by\n+  // this repository.\n+  //\n+  // Returns `\"git\"`.\n+  getType () {\n+    return 'git'\n+  }\n+\n+  // Public: Returns a {Promise} which resolves to the {String} path of the\n+  // repository.\n+  getPath () {\n+    return this.repoPromise.then(repo => repo.path().replace(/\\/$/, ''))\n+  }\n+\n+  // Public: Returns a {Promise} which resolves to the {String} working\n+  // directory path of the repository.\n+  getWorkingDirectory () {\n+    return this.repoPromise.then(repo => repo.workdir())\n+  }\n+\n+  // Public: Returns a {Promise} that resolves to true if at the root, false if\n+  // in a subfolder of the repository.\n+  isProjectAtRoot () {","path":"src/git-repository-async.js","position":143,"original_position":142,"commit_id":"35768f8595a228402417d9d4b93ac59a04dab064","original_commit_id":"15ab4b2383447d861f40e6602268e7c7358cd83a","user":{"login":"joshaber","id":13760,"avatar_url":"https://avatars.githubusercontent.com/u/13760?v=3","gravatar_id":"","url":"https://api.github.com/users/joshaber","html_url":"https://github.com/joshaber","followers_url":"https://api.github.com/users/joshaber/followers","following_url":"https://api.github.com/users/joshaber/following{/other_user}","gists_url":"https://api.github.com/users/joshaber/gists{/gist_id}","starred_url":"https://api.github.com/users/joshaber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/joshaber/subscriptions","organizations_url":"https://api.github.com/users/joshaber/orgs","repos_url":"https://api.github.com/users/joshaber/repos","events_url":"https://api.github.com/users/joshaber/events{/privacy}","received_events_url":"https://api.github.com/users/joshaber/received_events","type":"User","site_admin":true},"body":"Yup, good catch :+1: ","created_at":"2015-12-10T22:58:47Z","updated_at":"2015-12-10T22:58:47Z","html_url":"https://github.com/atom/atom/pull/9213#discussion_r47299814","pull_request_url":"https://api.github.com/repos/atom/atom/pulls/9213","_links":{"self":{"href":"https://api.github.com/repos/atom/atom/pulls/comments/47299814"},"html":{"href":"https://github.com/atom/atom/pull/9213#discussion_r47299814"},"pull_request":{"href":"https://api.github.com/repos/atom/atom/pulls/9213"}}},"pull_request":{"url":"https://api.github.com/repos/atom/atom/pulls/9213","id":48088519,"html_url":"https://github.com/atom/atom/pull/9213","diff_url":"https://github.com/atom/atom/pull/9213.diff","patch_url":"https://github.com/atom/atom/pull/9213.patch","issue_url":"https://api.github.com/repos/atom/atom/issues/9213","number":9213,"state":"open","locked":false,"title":"Async Git Repository class","user":{"login":"thedaniel","id":1476,"avatar_url":"https://avatars.githubusercontent.com/u/1476?v=3","gravatar_id":"","url":"https://api.github.com/users/thedaniel","html_url":"https://github.com/thedaniel","followers_url":"https://api.github.com/users/thedaniel/followers","following_url":"https://api.github.com/users/thedaniel/following{/other_user}","gists_url":"https://api.github.com/users/thedaniel/gists{/gist_id}","starred_url":"https://api.github.com/users/thedaniel/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/thedaniel/subscriptions","organizations_url":"https://api.github.com/users/thedaniel/orgs","repos_url":"https://api.github.com/users/thedaniel/repos","events_url":"https://api.github.com/users/thedaniel/events{/privacy}","received_events_url":"https://api.github.com/users/thedaniel/received_events","type":"User","site_admin":true},"body":"This is an experimental spike of an alternate GitRepository class that uses [nodegit](http://www.nodegit.org/) rather than [git-utils](https://github.com/atom/git-utils). We would love to speed up the responsiveness of various aspects of Atom (tree-view, etc) by doing more Git operations asynchronously, and it would also be nice to get out of the business of maintaining our own libgit2 wrapper.\r\n\r\nHere are a few notes about the reasoning behind some decisions here:\r\n\r\n- It follows the existing GitRepository API closely, changing everything sync to Promise-based async.\r\n  - The assumption is that having a close mapping between the sync and aysnc APIs will make it less painful to refactor client packages.\r\n  - I'm not 100% sure we should keep this design, so it's a tradeoff between design quality and incurred labor. Ideally we'll keep this private and experimental all the way until Atom 2.0 so that we can change it as we need to.\r\n- The class is written in ES6, because Coffeescript, while lovely, is the past, and the community seems to have consensus on transpilers like babel.\r\n  - This includes an .eslintrc, because I've found it extremely useful.\r\n  - The spec is still in coffee because I thought it might be easier to just modify it as I go to make sure I had the same coverage on both classes, though at the moment I wish I'd run it through a coffee -> js converter first.\r\n- I haven't done a lot of ES6 or constructed a Promise-based API for anything serious before, so I'm sure to misuse some APIs.\r\n- We'll need to keep the sync GitRepository class around for backwards compat, so I haven't decided quite how to integrate this with the current provider system.\r\n  - Perhaps we add a private method on the sync repo like `GitRepository.async` that returns the Promise for an async repo, so that internal callers can use async without changing anything in the `GitRepositoryProvider`?\r\n- Documentation is sparse because I wasn't sure this was a good path to follow until today. I'll flesh it out more as I go.\r\n\r\ncc @atom/core @orderedlist \r\n\r\n- [ ] Determine a good test case (even if manual / subjective) for git-related UI responsiveness\r\n  - many simultaneous changes, check tree-view update?\r\n  - simulate degraded disk performance / network drive?\r\n- [ ] cf #9240 \r\n","created_at":"2015-10-19T15:41:08Z","updated_at":"2015-12-10T22:58:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"02b2c2503228fed583932c2b5dbb913f6426428b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/atom/atom/pulls/9213/commits","review_comments_url":"https://api.github.com/repos/atom/atom/pulls/9213/comments","review_comment_url":"https://api.github.com/repos/atom/atom/pulls/comments{/number}","comments_url":"https://api.github.com/repos/atom/atom/issues/9213/comments","statuses_url":"https://api.github.com/repos/atom/atom/statuses/35768f8595a228402417d9d4b93ac59a04dab064","head":{"label":"atom:dh-async-repo","ref":"dh-async-repo","sha":"35768f8595a228402417d9d4b93ac59a04dab064","user":{"login":"atom","id":1089146,"avatar_url":"https://avatars.githubusercontent.com/u/1089146?v=3","gravatar_id":"","url":"https://api.github.com/users/atom","html_url":"https://github.com/atom","followers_url":"https://api.github.com/users/atom/followers","following_url":"https://api.github.com/users/atom/following{/other_user}","gists_url":"https://api.github.com/users/atom/gists{/gist_id}","starred_url":"https://api.github.com/users/atom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/atom/subscriptions","organizations_url":"https://api.github.com/users/atom/orgs","repos_url":"https://api.github.com/users/atom/repos","events_url":"https://api.github.com/users/atom/events{/privacy}","received_events_url":"https://api.github.com/users/atom/received_events","type":"Organization","site_admin":false},"repo":{"id":3228505,"name":"atom","full_name":"atom/atom","owner":{"login":"atom","id":1089146,"avatar_url":"https://avatars.githubusercontent.com/u/1089146?v=3","gravatar_id":"","url":"https://api.github.com/users/atom","html_url":"https://github.com/atom","followers_url":"https://api.github.com/users/atom/followers","following_url":"https://api.github.com/users/atom/following{/other_user}","gists_url":"https://api.github.com/users/atom/gists{/gist_id}","starred_url":"https://api.github.com/users/atom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/atom/subscriptions","organizations_url":"https://api.github.com/users/atom/orgs","repos_url":"https://api.github.com/users/atom/repos","events_url":"https://api.github.com/users/atom/events{/privacy}","received_events_url":"https://api.github.com/users/atom/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/atom/atom","description":"The hackable text editor","fork":false,"url":"https://api.github.com/repos/atom/atom","forks_url":"https://api.github.com/repos/atom/atom/forks","keys_url":"https://api.github.com/repos/atom/atom/keys{/key_id}","collaborators_url":"https://api.github.com/repos/atom/atom/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/atom/atom/teams","hooks_url":"https://api.github.com/repos/atom/atom/hooks","issue_events_url":"https://api.github.com/repos/atom/atom/issues/events{/number}","events_url":"https://api.github.com/repos/atom/atom/events","assignees_url":"https://api.github.com/repos/atom/atom/assignees{/user}","branches_url":"https://api.github.com/repos/atom/atom/branches{/branch}","tags_url":"https://api.github.com/repos/atom/atom/tags","blobs_url":"https://api.github.com/repos/atom/atom/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/atom/atom/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/atom/atom/git/refs{/sha}","trees_url":"https://api.github.com/repos/atom/atom/git/trees{/sha}","statuses_url":"https://api.github.com/repos/atom/atom/statuses/{sha}","languages_url":"https://api.github.com/repos/atom/atom/languages","stargazers_url":"https://api.github.com/repos/atom/atom/stargazers","contributors_url":"https://api.github.com/repos/atom/atom/contributors","subscribers_url":"https://api.github.com/repos/atom/atom/subscribers","subscription_url":"https://api.github.com/repos/atom/atom/subscription","commits_url":"https://api.github.com/repos/atom/atom/commits{/sha}","git_commits_url":"https://api.github.com/repos/atom/atom/git/commits{/sha}","comments_url":"https://api.github.com/repos/atom/atom/comments{/number}","issue_comment_url":"https://api.github.com/repos/atom/atom/issues/comments{/number}","contents_url":"https://api.github.com/repos/atom/atom/contents/{+path}","compare_url":"https://api.github.com/repos/atom/atom/compare/{base}...{head}","merges_url":"https://api.github.com/repos/atom/atom/merges","archive_url":"https://api.github.com/repos/atom/atom/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/atom/atom/downloads","issues_url":"https://api.github.com/repos/atom/atom/issues{/number}","pulls_url":"https://api.github.com/repos/atom/atom/pulls{/number}","milestones_url":"https://api.github.com/repos/atom/atom/milestones{/number}","notifications_url":"https://api.github.com/repos/atom/atom/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/atom/atom/labels{/name}","releases_url":"https://api.github.com/repos/atom/atom/releases{/id}","created_at":"2012-01-20T18:18:21Z","updated_at":"2015-12-10T22:57:44Z","pushed_at":"2015-12-10T22:58:05Z","git_url":"git://github.com/atom/atom.git","ssh_url":"git@github.com:atom/atom.git","clone_url":"https://github.com/atom/atom.git","svn_url":"https://github.com/atom/atom","homepage":"https://atom.io","size":339558,"stargazers_count":22896,"watchers_count":22896,"language":"CoffeeScript","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3924,"mirror_url":null,"open_issues_count":1614,"forks":3924,"open_issues":1614,"watchers":22896,"default_branch":"master"}},"base":{"label":"atom:master","ref":"master","sha":"26163f2195a10ce17da96177e823bb8dcf1e974d","user":{"login":"atom","id":1089146,"avatar_url":"https://avatars.githubusercontent.com/u/1089146?v=3","gravatar_id":"","url":"https://api.github.com/users/atom","html_url":"https://github.com/atom","followers_url":"https://api.github.com/users/atom/followers","following_url":"https://api.github.com/users/atom/following{/other_user}","gists_url":"https://api.github.com/users/atom/gists{/gist_id}","starred_url":"https://api.github.com/users/atom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/atom/subscriptions","organizations_url":"https://api.github.com/users/atom/orgs","repos_url":"https://api.github.com/users/atom/repos","events_url":"https://api.github.com/users/atom/events{/privacy}","received_events_url":"https://api.github.com/users/atom/received_events","type":"Organization","site_admin":false},"repo":{"id":3228505,"name":"atom","full_name":"atom/atom","owner":{"login":"atom","id":1089146,"avatar_url":"https://avatars.githubusercontent.com/u/1089146?v=3","gravatar_id":"","url":"https://api.github.com/users/atom","html_url":"https://github.com/atom","followers_url":"https://api.github.com/users/atom/followers","following_url":"https://api.github.com/users/atom/following{/other_user}","gists_url":"https://api.github.com/users/atom/gists{/gist_id}","starred_url":"https://api.github.com/users/atom/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/atom/subscriptions","organizations_url":"https://api.github.com/users/atom/orgs","repos_url":"https://api.github.com/users/atom/repos","events_url":"https://api.github.com/users/atom/events{/privacy}","received_events_url":"https://api.github.com/users/atom/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/atom/atom","description":"The hackable text editor","fork":false,"url":"https://api.github.com/repos/atom/atom","forks_url":"https://api.github.com/repos/atom/atom/forks","keys_url":"https://api.github.com/repos/atom/atom/keys{/key_id}","collaborators_url":"https://api.github.com/repos/atom/atom/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/atom/atom/teams","hooks_url":"https://api.github.com/repos/atom/atom/hooks","issue_events_url":"https://api.github.com/repos/atom/atom/issues/events{/number}","events_url":"https://api.github.com/repos/atom/atom/events","assignees_url":"https://api.github.com/repos/atom/atom/assignees{/user}","branches_url":"https://api.github.com/repos/atom/atom/branches{/branch}","tags_url":"https://api.github.com/repos/atom/atom/tags","blobs_url":"https://api.github.com/repos/atom/atom/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/atom/atom/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/atom/atom/git/refs{/sha}","trees_url":"https://api.github.com/repos/atom/atom/git/trees{/sha}","statuses_url":"https://api.github.com/repos/atom/atom/statuses/{sha}","languages_url":"https://api.github.com/repos/atom/atom/languages","stargazers_url":"https://api.github.com/repos/atom/atom/stargazers","contributors_url":"https://api.github.com/repos/atom/atom/contributors","subscribers_url":"https://api.github.com/repos/atom/atom/subscribers","subscription_url":"https://api.github.com/repos/atom/atom/subscription","commits_url":"https://api.github.com/repos/atom/atom/commits{/sha}","git_commits_url":"https://api.github.com/repos/atom/atom/git/commits{/sha}","comments_url":"https://api.github.com/repos/atom/atom/comments{/number}","issue_comment_url":"https://api.github.com/repos/atom/atom/issues/comments{/number}","contents_url":"https://api.github.com/repos/atom/atom/contents/{+path}","compare_url":"https://api.github.com/repos/atom/atom/compare/{base}...{head}","merges_url":"https://api.github.com/repos/atom/atom/merges","archive_url":"https://api.github.com/repos/atom/atom/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/atom/atom/downloads","issues_url":"https://api.github.com/repos/atom/atom/issues{/number}","pulls_url":"https://api.github.com/repos/atom/atom/pulls{/number}","milestones_url":"https://api.github.com/repos/atom/atom/milestones{/number}","notifications_url":"https://api.github.com/repos/atom/atom/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/atom/atom/labels{/name}","releases_url":"https://api.github.com/repos/atom/atom/releases{/id}","created_at":"2012-01-20T18:18:21Z","updated_at":"2015-12-10T22:57:44Z","pushed_at":"2015-12-10T22:58:05Z","git_url":"git://github.com/atom/atom.git","ssh_url":"git@github.com:atom/atom.git","clone_url":"https://github.com/atom/atom.git","svn_url":"https://github.com/atom/atom","homepage":"https://atom.io","size":339558,"stargazers_count":22896,"watchers_count":22896,"language":"CoffeeScript","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3924,"mirror_url":null,"open_issues_count":1614,"forks":3924,"open_issues":1614,"watchers":22896,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/atom/atom/pulls/9213"},"html":{"href":"https://github.com/atom/atom/pull/9213"},"issue":{"href":"https://api.github.com/repos/atom/atom/issues/9213"},"comments":{"href":"https://api.github.com/repos/atom/atom/issues/9213/comments"},"review_comments":{"href":"https://api.github.com/repos/atom/atom/pulls/9213/comments"},"review_comment":{"href":"https://api.github.com/repos/atom/atom/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/atom/atom/pulls/9213/commits"},"statuses":{"href":"https://api.github.com/repos/atom/atom/statuses/35768f8595a228402417d9d4b93ac59a04dab064"}}}}