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}