Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

Async Git Repository class #9213

Merged
merged 297 commits into from
Jan 15, 2016
Merged

Async Git Repository class #9213

merged 297 commits into from
Jan 15, 2016

Conversation

thedaniel
Copy link
Contributor

This is an experimental spike of an alternate GitRepository class that uses nodegit rather than 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.

Here are a few notes about the reasoning behind some decisions here:

  • It follows the existing GitRepository API closely, changing everything sync to Promise-based async.
    • The assumption is that having a close mapping between the sync and aysnc APIs will make it less painful to refactor client packages.
    • 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.
  • The class is written in ES6, because Coffeescript, while lovely, is the past, and the community seems to have consensus on transpilers like babel.
    • This includes an .eslintrc, because I've found it extremely useful.
    • 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.
  • 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.
  • 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.
    • 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?
  • 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.

cc @atom/core @orderedlist

@nathansobo
Copy link
Contributor

What's the plan for transitioning to this API? I was thinking our existing synchronous GitRepository could have an async property that points to this new version. We can then encourage users of today's APIs to use the async version like this:

let repository = someMethodReturningARepository().async

When we hit Atom 2.0 and transition to the repository always being async, we can retain the async property and just point it to self, such that repository.async === repository returns true.

However, I'm not sure how this all meshes with your idea of keeping this API private for a while. In my mind, we should start transitioning the community to any async APIs we feel confident about. We might not want to make every method on the class public, but I'd like to deprecate existing synchronous functionality as soon as we can.

@thedaniel
Copy link
Contributor Author

I was thinking our existing synchronous GitRepository could have an async property that points to this new version.

Yep, I agree with this, it's suggested in the bullet points above.

However, I'm not sure how this all meshes with your idea of keeping this API private for a while. In my mind, we should start transitioning the community to any async APIs we feel confident about.

This is not necessarily a conflict with how I feel, I'm just not expecting we'll feel confident about the APIs for a while.


onSuccess = jasmine.createSpy('onSuccess')

waitsForPromise ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you also write the specs in ES6, async/await syntax could really clean up these async tests...

it('does something async', function () {
  waitsForPromise(async function() {
    expect(await repo.getPath()).toBe('/some/path')
  })
})

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's ES7+ only

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, which we have access to 😄

@nathansobo
Copy link
Contributor

⚡ Thanks for moving us in the right direction on this.

@nathansobo
Copy link
Contributor

it's suggested in the bullet points above.

Sorry, I didn't comprehend it initially for some reason. Of course async will have to be a promise, and when we transition all our repository-returning APIs to be asynchronous, we'll have to have an async property on the returned promise that references the original promise.

@smashwilson
Copy link
Contributor

Oh hey, this will be excellent for packages that integrate with git. 👍 😁

@alexandernst
Copy link

❤️ ES6 instead of ☕script!

@thedaniel
Copy link
Contributor Author

Thought: it seems like a good place to start using this in prod & packages is to proxy all the did-change-status events from the sync implementation to the async implementation. At this moment I'm just writing tests that assert the events fire correctly in async-land at all and calling through from sync to async in a few methods (e.g. getPathStatus(path)) just so the side-effects happen, but ultimately we could remove anything solely concerned with generating events out of the sync implementation entirely and just re-emit the ones generated by the async implementation.

return new GitRepositoryAsync(path, options)
}

constructor (path, options) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💄 🎨 What do you think about removing the spaces in front of the arguments? https://github.com/airbnb/javascript#18.3 Just about style. Really good work! Love the ES6 ❤️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch - this currently conforms to JS Standard Style (a very presumptuous name, isn't it?) rather than AirBnB style and I'm happy with Standard.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the resource. Didn't knew about it! Looks of course also good 👍 Just important that there is one style over the whole repository 👍 👏

@thedaniel
Copy link
Contributor Author

Thanks for the feedback @steelbrain!


getPath () {
return this.repoPromise.then((repo) => {
return Promise.resolve(repo.path().replace(/\/$/, ''))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to replace most of these Promise.resolves here with just values.

thedaniel added a commit to atom/tree-view that referenced this pull request Oct 23, 2015
This will break a lot of things, and depends on
atom/atom#9213
@ghost
Copy link

ghost commented Oct 23, 2015

You guys really hate placing semicolons don't you? :D

@SeanJM
Copy link

SeanJM commented Oct 23, 2015

By not adding semi colons, are you adding overhead to the JIT?

On Oct 23, 2015, at 12:04 PM, Alex Cruz notifications@github.com wrote:

You guys really hate placing semicolons don't you? :D


Reply to this email directly or view it on GitHub #9213 (comment).

@ghost
Copy link

ghost commented Oct 23, 2015

@SeanJM I don't know why github doesn't use them in their code, is this because they have most of their code in Ruby or Coffescript doesn't allow them?

@SeanJM
Copy link

SeanJM commented Oct 23, 2015

https://www.quora.com/Are-there-performance-differences-between-using-semi-colons-and-not-using-semi-colons-in-JavaScript https://www.quora.com/Are-there-performance-differences-between-using-semi-colons-and-not-using-semi-colons-in-JavaScript
According to this the answer is no.

On Oct 23, 2015, at 1:11 PM, Alex Cruz notifications@github.com wrote:

@SeanJM https://github.com/SeanJM I don't know why github doesn't use them in their code, is this because they have most of their code in Ruby?


Reply to this email directly or view it on GitHub #9213 (comment).

this._gitUtilsRepo = GitUtils.open(path) // TODO remove after porting ::relativize
this.repoPromise = Git.Repository.open(path)

var {project, refreshOnWindowFocus} = options
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be let (or const), doesn't look like we want or need hoisting here.

@joshaber
Copy link
Contributor

joshaber added a commit that referenced this pull request Jan 15, 2016
@joshaber joshaber merged commit 4d740d3 into master Jan 15, 2016
@joshaber joshaber deleted the dh-async-repo branch January 15, 2016 21:17
@kevinsawicki
Copy link
Contributor

Awesome work here 👍

@johnhaley81
Copy link

w00t!

g5pqtoi

@martindale
Copy link

Does this address #2456?

@50Wliu
Copy link
Contributor

50Wliu commented Mar 17, 2016

@martindale it would be great if you could download the 1.7.0 beta and find out :).

@martindale
Copy link

Subjectively, the problem appears to be worse with 1.7.0 beta. Atom now completely hangs when launching it inside of a remote mount, and eventually requests direction on whether to close or keep waiting.

@yannickcr
Copy link

Same problem here, Atom 1.6.0 or 1.7.0 beta now hangs up when trying to open a big git repository inside of a remote mount making Atom unusable in this configuration 😢

@bynite
Copy link

bynite commented Mar 24, 2016

Can't use Atom anymore. Probably because of this feature. I mounted a vagrant via SSHFS and Atom freezes when opening a file. Could you release a fix for this, please.

@mehcode
Copy link
Contributor

mehcode commented Mar 24, 2016

I'm wondering why we switched to using nodegit. I realize that maintaining a wrapper around a git executable is lame but nodegit wraps libgit2 which is glacial in comparison to git. This is completely anecdotal but I've always found it far slower.

Perhaps using something like https://www.npmjs.com/package/simple-git instead?

@thedaniel
Copy link
Contributor Author

@mehcode Before nodegit we were already using libgit2, with a sync api wrapper we were maintaining ourselves (https://github.com/atom/git-utils) - so we can't blame any performance regressions on libgit2 here.

@mehcode
Copy link
Contributor

mehcode commented Mar 24, 2016

Ah. I was under the impression we were just doing shell execution.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.