| 'use strict'; |
| |
| class CommitLog extends DataModelObject { |
| constructor(id, rawData) |
| { |
| console.assert(parseInt(id) == id); |
| super(id); |
| console.assert(id == rawData.id) |
| this._repository = rawData.repository; |
| console.assert(this._repository instanceof Repository); |
| this._rawData = rawData; |
| this._ownedCommits = null; |
| this._ownerCommit = null; |
| this._ownedCommitByOwnedRepository = new Map; |
| } |
| |
| updateSingleton(rawData) |
| { |
| super.updateSingleton(rawData); |
| |
| console.assert(+this._rawData['time'] == +rawData['time']); |
| console.assert(this._rawData['revision'] == rawData['revision']); |
| |
| if (rawData.authorName) |
| this._rawData.authorName = rawData.authorName; |
| if (rawData.message) |
| this._rawData.message = rawData.message; |
| if (rawData.ownsCommits) |
| this._rawData.ownsCommits = rawData.ownsCommits; |
| if (rawData.order) |
| this._rawData.order = rawData.order; |
| if (rawData.testability) |
| this._rawData.testability = rawData.testability; |
| } |
| |
| repository() { return this._repository; } |
| time() { return new Date(this._rawData['time']); } |
| hasCommitTime() { return this._rawData['time'] > 0 && this._rawData['time'] != null; } |
| testability() { return this._rawData['testability']; } |
| author() { return this._rawData['authorName']; } |
| revision() { return this._rawData['revision']; } |
| message() { return this._rawData['message']; } |
| url() { return this._repository.urlForRevision(this._rawData['revision']); } |
| ownsCommits() { return this._rawData['ownsCommits']; } |
| ownedCommits() { return this._ownedCommits; } |
| ownerCommit() { return this._ownerCommit; } |
| order() { return this._rawData['order']; } |
| hasCommitOrder() { return this._rawData['order'] != null; } |
| setOwnerCommits(ownerCommit) { this._ownerCommit = ownerCommit; } |
| |
| label() |
| { |
| const revision = this.revision(); |
| if (parseInt(revision) == revision) // e.g. r12345 |
| return 'r' + revision; |
| if (revision.length == 40) // e.g. git hash |
| return revision.substring(0, 12); |
| return revision; |
| } |
| title() { return this._repository.name() + ' at ' + this.label(); } |
| |
| diff(previousCommit) |
| { |
| if (this == previousCommit) |
| previousCommit = null; |
| |
| const repository = this._repository; |
| if (!previousCommit) |
| return {repository: repository, label: this.label(), url: this.url()}; |
| |
| const to = this.revision(); |
| const from = previousCommit.revision(); |
| let label = null; |
| if (parseInt(from) == from)// e.g. r12345. |
| label = `r${from}-r${this.revision()}`; |
| else if (to.length == 40) // e.g. git hash |
| label = `${from.substring(0, 12)}..${to.substring(0, 12)}`; |
| else |
| label = `${from} - ${to}`; |
| |
| return {repository: repository, label: label, url: repository.urlForRevisionRange(from, to)}; |
| } |
| |
| static fetchLatestCommitForPlatform(repository, platform) |
| { |
| console.assert(repository instanceof Repository); |
| console.assert(platform instanceof Platform); |
| return this.cachedFetch(`/api/commits/${repository.id()}/latest`, {platform: platform.id()}).then((data) => { |
| const commits = data['commits']; |
| if (!commits || !commits.length) |
| return null; |
| const rawData = commits[0]; |
| rawData.repository = repository; |
| return CommitLog.ensureSingleton(rawData.id, rawData); |
| }); |
| } |
| |
| static hasOrdering(firstCommit, secondCommit) |
| { |
| return (firstCommit.hasCommitTime() && secondCommit.hasCommitTime()) || |
| (firstCommit.hasCommitOrder() && secondCommit.hasCommitOrder()); |
| } |
| |
| static orderTwoCommits(firstCommit, secondCommit) |
| { |
| console.assert(CommitLog.hasOrdering(firstCommit, secondCommit)); |
| const firstCommitSmaller = firstCommit.hasCommitTime() && secondCommit.hasCommitTime() ? |
| firstCommit.time() < secondCommit.time() : firstCommit.order() < secondCommit.order(); |
| return firstCommitSmaller ? [firstCommit, secondCommit] : [secondCommit, firstCommit]; |
| } |
| |
| ownedCommitForOwnedRepository(ownedRepository) { return this._ownedCommitByOwnedRepository.get(ownedRepository); } |
| |
| fetchOwnedCommits() |
| { |
| if (!this.repository().ownedRepositories()) |
| return Promise.reject(); |
| |
| if (!this.ownsCommits()) |
| return Promise.reject(); |
| |
| if (this._ownedCommits) |
| return Promise.resolve(this._ownedCommits); |
| |
| return CommitLog.cachedFetch(`../api/commits/${this.repository().id()}/owned-commits?owner-revision=${escape(this.revision())}`).then((data) => { |
| this._ownedCommits = CommitLog._constructFromRawData(data); |
| this._ownedCommits.forEach((ownedCommit) => { |
| ownedCommit.setOwnerCommits(this); |
| this._ownedCommitByOwnedRepository.set(ownedCommit.repository(), ownedCommit); |
| }); |
| return this._ownedCommits; |
| }); |
| } |
| |
| _buildOwnedCommitMap() |
| { |
| const ownedCommitMap = new Map; |
| for (const commit of this._ownedCommits) |
| ownedCommitMap.set(commit.repository(), commit); |
| return ownedCommitMap; |
| } |
| |
| static ownedCommitDifferenceForOwnerCommits(...commits) |
| { |
| console.assert(commits.length >= 2); |
| |
| const ownedCommitRepositories = new Set; |
| const ownedCommitMapList = commits.map((commit) => { |
| console.assert(commit); |
| console.assert(commit._ownedCommits); |
| const ownedCommitMap = commit._buildOwnedCommitMap(); |
| for (const repository of ownedCommitMap.keys()) |
| ownedCommitRepositories.add(repository); |
| return ownedCommitMap; |
| }); |
| |
| const difference = new Map; |
| ownedCommitRepositories.forEach((ownedCommitRepository) => { |
| const ownedCommits = ownedCommitMapList.map((ownedCommitMap) => ownedCommitMap.get(ownedCommitRepository)); |
| const uniqueOwnedCommits = new Set(ownedCommits); |
| if (uniqueOwnedCommits.size > 1) |
| difference.set(ownedCommitRepository, ownedCommits); |
| }); |
| return difference; |
| } |
| |
| static async fetchBetweenRevisions(repository, precedingRevision, lastRevision) |
| { |
| // FIXME: The cache should be smarter about fetching a range within an already fetched range, etc... |
| // FIXME: We should evict some entries from the cache in cachedFetch. |
| const data = await this.cachedFetch(`/api/commits/${repository.id()}/`, {precedingRevision, lastRevision}); |
| return this._constructFromRawData(data); |
| } |
| |
| static async fetchForSingleRevision(repository, revision, prefixMatch=false) |
| { |
| const commit = repository.commitForRevision(revision); |
| if (commit) |
| return [commit]; |
| |
| let params = {}; |
| if (prefixMatch) |
| params['prefix-match'] = true; |
| const data = await this.cachedFetch(`/api/commits/${repository.id()}/${revision}`, params); |
| return this._constructFromRawData(data); |
| } |
| |
| static _constructFromRawData(data) |
| { |
| return data['commits'].map((rawData) => { |
| const repositoryId = rawData.repository; |
| const repository = Repository.findById(repositoryId); |
| rawData.repository = repository; |
| const commit = CommitLog.ensureSingleton(rawData.id, rawData); |
| repository.setCommitForRevision(commit.revision(), commit); |
| return commit; |
| }); |
| } |
| } |
| |
| if (typeof module != 'undefined') |
| module.exports.CommitLog = CommitLog; |