Organize similarly to git-repository.coffee so we can more easily tell what we're missing.

This commit is contained in:
joshaber
2015-12-02 15:28:09 -05:00
parent 428797c393
commit fe4d3601d5

View File

@@ -59,14 +59,209 @@ export default class GitRepositoryAsync {
}
}
// Event subscription
// ==================
onDidDestroy (callback) {
return this.emitter.on('did-destroy', callback)
}
onDidChangeStatus (callback) {
return this.emitter.on('did-change-status', callback)
}
onDidChangeStatuses (callback) {
return this.emitter.on('did-change-statuses', 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.repoPromise.then(repo => repo.path().replace(/\/$/, ''))
}
isPathIgnored (_path) {
return this.repoPromise.then(repo => Git.Ignore.pathIsIgnored(repo, _path))
// Public: Returns a {Promise} which resolves to the {String} working
// directory path of the repository.
getWorkingDirectory () {
throw new Error('Unimplemented')
}
// Public: Returns a {Promise} that resolves to true if at the root, false if
// in a subfolder of the repository.
isProjectAtRoot () {
if (!this.projectAtRoot && this.project) {
this.projectAtRoot = Promise.resolve(() => {
return this.repoPromise.then(repo => this.project.relativize(repo.workdir))
})
}
return this.projectAtRoot
}
// Public: Makes a path relative to the repository's working directory.
relativize (_path, workingDirectory) {
// Cargo-culted from git-utils. The original implementation also handles
// this.openedWorkingDirectory, which is set by git-utils when the
// repository is opened. Those branches of the if tree aren't included here
// yet, but if we determine we still need that here it should be simple to
// port.
//
// The original implementation also handled null workingDirectory as it
// pulled it from a sync function that could return null. We require it
// to be passed here.
if (!_path || !workingDirectory) {
return _path
}
if (process.platform === 'win32') {
_path = _path.replace(/\\/g, '/')
} else {
if (_path[0] !== '/') {
return _path
}
}
if (!/\/$/.test(workingDirectory)) {
workingDirectory = `${workingDirectory}/`
}
if (this.isCaseInsensitive) {
const lowerCasePath = _path.toLowerCase()
workingDirectory = workingDirectory.toLowerCase()
if (lowerCasePath.indexOf(workingDirectory) === 0) {
return _path.substring(workingDirectory.length)
} else {
if (lowerCasePath === workingDirectory) {
return ''
}
}
}
return _path
}
// Public: Returns true if the given branch exists.
hasBranch (branch) {
throw new Error('Unimplemented')
}
// 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 {String}.
getShortHead (path) {
throw new Error('Unimplemented')
}
// 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.repoPromise
.then(repo => repo.openIndex())
.then(index => {
const entry = index.getByPath(_path)
const submoduleMode = 57344 // TODO compose this from libgit2 constants
return entry.mode === submoduleMode
})
}
// 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.
getAheadBehindCount (reference, path) {
throw new Error('Unimplemented')
}
// 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 an {Object} with the following keys:
// * `ahead` The {Number} of commits ahead.
// * `behind` The {Number} of commits behind.
getCachedUpstreamAheadBehindCount (path) {
throw new Error('Unimplemented')
}
// 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.
getConfigValue (key, path) {
throw new Error('Unimplemented')
}
// Public: Returns the origin url of the repository.
//
// * `path` (optional) {String} path in the repository to get this information
// for, only needed if the repository has submodules.
getOriginURL (path) {
throw new Error('Unimplemented')
}
// 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 {String} branch name such as `refs/remotes/origin/master`.
getUpstreamBranch (path) {
throw new Error('Unimplemented')
}
// 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 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) {
throw new Error('Unimplemented')
}
// Public: Returns the current {String} 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.
getReferenceTarget (reference, path) {
throw new Error('Unimplemented')
}
// Reading Status
// ==============
isPathModified (_path) {
return this._filterStatusesByPath(_path).then(statuses => {
return statuses.filter(status => status.isModified()).length > 0
@@ -79,29 +274,36 @@ export default class GitRepositoryAsync {
})
}
checkoutHead (_path) {
return this.repoPromise
.then(repo => {
const checkoutOptions = new Git.CheckoutOptions()
checkoutOptions.paths = [this.relativize(_path, repo.workdir())]
checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH
return Git.Checkout.head(repo, checkoutOptions)
})
.then(() => this.refreshStatusForPath(_path))
isPathIgnored (_path) {
return this.repoPromise.then(repo => Git.Ignore.pathIsIgnored(repo, _path))
}
checkoutHeadForEditor (editor) {
return new Promise((resolve, reject) => {
const filePath = editor.getPath()
if (filePath) {
if (editor.buffer.isModified()) {
editor.buffer.reload()
}
resolve(filePath)
} else {
reject()
}
}).then(filePath => this.checkoutHead(filePath))
// 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) {
let relativePath
// XXX _filterSBD already gets repoPromise
return this.repoPromise
.then(repo => {
relativePath = this.relativize(directoryPath, repo.workdir())
return this._filterStatusesByDirectory(relativePath)
})
.then(statuses => {
return Promise.all(statuses.map(s => s.statusBit())).then(bits => {
let directoryStatus = 0
const filteredBits = bits.filter(b => b > 0)
if (filteredBits.length > 0) {
filteredBits.forEach(bit => directoryStatus |= bit)
}
return directoryStatus
})
})
}
// Refresh the status bit for the given path.
@@ -142,34 +344,83 @@ export default class GitRepositoryAsync {
return this.refreshStatusForPath(_path)
}
// Get the status of a directory in the repository's working directory.
// Public: Get the cached status for the given path.
//
// * `directoryPath` The {String} path to check.
// * `path` A {String} path in the repository, relative or absolute.
//
// Returns a promise resolving to a {Number} representing the status. This value can be passed to
// {::isStatusModified} or {::isStatusNew} to get more information.
// Returns a {Promise} which resolves to a status {Number} or null if the
// path is not in the cache.
getCachedPathStatus (_path) {
return this.repoPromise
.then(repo => this.relativize(_path, repo.workdir()))
.then(relativePath => this.pathStatusCache[relativePath])
}
getDirectoryStatus (directoryPath) {
let relativePath
// XXX _filterSBD already gets repoPromise
isStatusNew (statusBit) {
return (statusBit & newStatusFlags) > 0
}
isStatusModified (statusBit) {
return (statusBit & modifiedStatusFlags) > 0
}
isStatusStaged (statusBit) {
return (statusBit & indexStatusFlags) > 0
}
isStatusIgnored (statusBit) {
return (statusBit & (1 << 14)) > 0
}
isStatusDeleted (statusBit) {
return (statusBit & deletedStatusFlags) > 0
}
// 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 -- <path>
// git checkout HEAD -- <path>
// ```
//
// * `path` The {String} path to checkout.
//
// Returns a {Promise} that resolves or rejects depending on whether the
// method was successful.
checkoutHead (_path) {
return this.repoPromise
.then(repo => {
relativePath = this.relativize(directoryPath, repo.workdir())
return this._filterStatusesByDirectory(relativePath)
})
.then(statuses => {
return Promise.all(statuses.map(s => s.statusBit())).then(bits => {
let directoryStatus = 0
const filteredBits = bits.filter(b => b > 0)
if (filteredBits.length > 0) {
filteredBits.forEach(bit => directoryStatus |= bit)
}
return directoryStatus
})
const checkoutOptions = new Git.CheckoutOptions()
checkoutOptions.paths = [this.relativize(_path, repo.workdir())]
checkoutOptions.checkoutStrategy = Git.Checkout.STRATEGY.FORCE | Git.Checkout.STRATEGY.DISABLE_PATHSPEC_MATCH
return Git.Checkout.head(repo, checkoutOptions)
})
.then(() => this.refreshStatusForPath(_path))
}
checkoutHeadForEditor (editor) {
return new Promise((resolve, reject) => {
const filePath = editor.getPath()
if (filePath) {
if (editor.buffer.isModified()) {
editor.buffer.reload()
}
resolve(filePath)
} else {
reject()
}
}).then(filePath => this.checkoutHead(filePath))
}
// Private
// =======
// Get the current branch and update this.branch.
//
// Returns :: Promise<String>
@@ -241,74 +492,6 @@ export default class GitRepositoryAsync {
return
}
relativize (_path, workingDirectory) {
// Cargo-culted from git-utils. The original implementation also handles
// this.openedWorkingDirectory, which is set by git-utils when the
// repository is opened. Those branches of the if tree aren't included here
// yet, but if we determine we still need that here it should be simple to
// port.
//
// The original implementation also handled null workingDirectory as it
// pulled it from a sync function that could return null. We require it
// to be passed here.
if (!_path || !workingDirectory) {
return _path
}
if (process.platform === 'win32') {
_path = _path.replace(/\\/g, '/')
} else {
if (_path[0] !== '/') {
return _path
}
}
if (!/\/$/.test(workingDirectory)) {
workingDirectory = `${workingDirectory}/`
}
if (this.isCaseInsensitive) {
const lowerCasePath = _path.toLowerCase()
workingDirectory = workingDirectory.toLowerCase()
if (lowerCasePath.indexOf(workingDirectory) === 0) {
return _path.substring(workingDirectory.length)
} else {
if (lowerCasePath === workingDirectory) {
return ''
}
}
}
return _path
}
getCachedPathStatus (_path) {
return this.repoPromise.then(repo => {
return this.pathStatusCache[this.relativize(_path, repo.workdir())]
})
}
isStatusNew (statusBit) {
return (statusBit & newStatusFlags) > 0
}
isStatusModified (statusBit) {
return (statusBit & modifiedStatusFlags) > 0
}
isStatusStaged (statusBit) {
return (statusBit & indexStatusFlags) > 0
}
isStatusIgnored (statusBit) {
return (statusBit & (1 << 14)) > 0
}
isStatusDeleted (statusBit) {
return (statusBit & deletedStatusFlags) > 0
}
_filterStatusesByPath (_path) {
// Surely I'm missing a built-in way to do this
let basePath = null
@@ -329,46 +512,4 @@ export default class GitRepositoryAsync {
return statuses.filter(status => status.path().indexOf(directoryPath) === 0)
})
}
// Event subscription
// ==================
onDidChangeStatus (callback) {
return this.emitter.on('did-change-status', callback)
}
onDidChangeStatuses (callback) {
return this.emitter.on('did-change-statuses', callback)
}
onDidDestroy (callback) {
return this.emitter.on('did-destroy', callback)
}
//
// Section: Repository Details
//
// Returns a {Promise} that resolves true if at the root, false if in a
// subfolder of the repository.
isProjectAtRoot () {
if (this.projectAtRoot === undefined) {
this.projectAtRoot = Promise.resolve(() => {
return this.repoPromise.then(repo => this.project.relativize(repo.workdir))
})
}
return this.projectAtRoot
}
// Returns a {Promise} that resolves true if the given path is a submodule in
// the repository.
isSubmodule (_path) {
return this.repoPromise
.then(repo => repo.openIndex())
.then(index => {
const entry = index.getByPath(_path)
const submoduleMode = 57344 // TODO compose this from libgit2 constants
return entry.mode === submoduleMode
})
}
}