VBL5BQH7K5QZH3KCO4JBRDVYAN4YXYNAWETL2WVUD2PYHNZAGMTAC DLDMHQY6F53N2YXNDBQY77GAU2I4N2OWC3DL6L36T2SSAVW6YSXAC GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC 37OJKSWJFDRHNWQW6P7HSZX6OWZWVNCJ2IFT42O5TANQF7VOVX6AC FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC ISO7J5ZH5UB7NFZKTKKJQHQHCP4DWQ3F7SM2NDMVYJAGGIKDLX4QC FRFFQV7VNYKGCA7ZAOSRPC2HHYTAIZ6AGGR7A5QEV6QPAQGFDYGAC Q7FXTHVUPVAFMNY277C3NFJO3VXLZU5G6C6UYSD5QPURHSG3A7OQC RE4EKNSLYGCITZZEOPIRJAWTKIONGP7IY6S77BQO7JQL2CK27RZAC QXUEMZ3B2FUHFUC7ZZHJMH5FVWLEMEYXUMFA6JNXTJKIVZNMRIOAC A7IL6I3VCB3F3QMYQFPXBL33DN5XDGGQPZIBZRHYSQBSIUBBFQJQC OMZXJL6QA6INENIEAARSWYFHOPMLTP4WRCVI646GQVJVWCH3LENQC 6CR2EFUN7JXFHCBTNX3WWOOP4WFOCFO6KSPEBN6V6J5HFZO2LHNQC B43WNBLFFR2UQIH3C6KIZAQTAEQOQM3J3IYLGQMVGJHYOME73OKQC OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC 5AUENX2YJVFNKZUSPEPDNLLL7TKZS2WTFC6CABWSZK2EC4MNCRQAC 7L5LODGZ7AN4ZULDJZMLALD7PL6E57VZSNNSG67SFJARUJGCT47QC Q35OTML226J2HZLHOCPV5OY6ZUM2XU4RZBE5E3GDWVHVFASHFEJAC ZCRW57C5MSBXYGUMGQTZNHGHO4HGHFBICW53X5I2IMGP3H2CKWRQC MZYZIVHY5DUHVJH7YGENRAIKTKMZWJ5ANRVGFLLU7YTHXC2OXZJQC caching uses a bunch of storage space and takes a bit of more time at first loading.
caching uses a bunch of storage space and takes a bit of more time at first loading.pijul.log.refGroup.default=Defaultpijul.log.refGroup.current=Currentpijul.log.refGroup.channel=Channelpijul.log.refGroup.tag=Tagpijul.log.refGroup.tag_text={0}pijul.log.refGroup.mainChannel=mainpijul.log.refGroup.other=Other
package com.github.jonathanxd.dracon.utilimport com.intellij.openapi.vcs.FilePathimport com.intellij.openapi.vfs.VirtualFileimport com.intellij.vcsUtil.VcsUtilimport java.nio.file.Pathimport java.nio.file.Pathsfun VirtualFile.convertToNio(): Path =Paths.get(VcsUtil.getFilePath(this).path)fun FilePath.convertToNio(): Path =Paths.get(this.path)
package com.github.jonathanxd.dracon.utilimport com.intellij.openapi.project.Projectfun Project.pijul() =com.github.jonathanxd.dracon.pijul.pijul(this)fun Project.pijulVcs() =com.github.jonathanxd.dracon.pijulVcs(this)
package com.github.jonathanxd.dracon.utilimport com.github.jonathanxd.iutils.collection.view.ViewCollectionsimport com.github.jonathanxd.iutils.collection.wrapper.WrapperCollectionsimport com.intellij.util.containers.map2Arrayinterface BiDiMap<K, V> : Map<K, V> {val otherDirection: BiDiMap<V, K>}interface MutableBiDiMap<K, V> : BiDiMap<K, V>, MutableMap<K, V> {override val otherDirection: MutableBiDiMap<V, K>}class BiDiMapImpl<K, V>(val kvMap: Map<K, V>,val vkMap: Map<V, K>): BiDiMap<K, V> {constructor(map: Map<K, V>): this(map, map.createVKMap())override val entries: Set<Map.Entry<K, V>>get() = this.kvMap.entriesoverride val keys: Set<K>get() = this.kvMap.keysoverride val size: Intget() = this.kvMap.sizeoverride val values: Collection<V>get() = this.kvMap.valuesoverride fun containsKey(key: K): Boolean = this.kvMap.containsKey(key)override fun containsValue(value: V): Boolean = this.kvMap.containsValue(value)override fun get(key: K): V? = this.kvMap[key]override fun isEmpty(): Boolean = this.kvMap.isEmpty()override val otherDirection: BiDiMap<V, K> = OtherDirection()inner class OtherDirection: BiDiMap<V, K> {override val entries: Set<Map.Entry<V, K>>get() = this@BiDiMapImpl.vkMap.entriesoverride val keys: Set<V>get() = this@BiDiMapImpl.vkMap.keysoverride val size: Intget() = this@BiDiMapImpl.vkMap.sizeoverride val values: Collection<K>get() = this@BiDiMapImpl.vkMap.valuesoverride fun containsKey(key: V): Boolean = this@BiDiMapImpl.vkMap.containsKey(key)override fun containsValue(value: K): Boolean = this@BiDiMapImpl.vkMap.containsValue(value)override fun get(key: V): K? = this@BiDiMapImpl.vkMap[key]override fun isEmpty(): Boolean = this@BiDiMapImpl.vkMap.isEmpty()override val otherDirection: BiDiMap<K, V>get() = this@BiDiMapImpl}}class MutableBiDiMapImpl<K, V>(val kvMap: MutableMap<K, V>,val vkMap: MutableMap<V, K>): MutableBiDiMap<K, V> {constructor(map: MutableMap<K, V>): this(map, map.createVKMutableMap())override val entries: MutableSet<MutableMap.MutableEntry<K, V>>get() = ViewCollections.setMapped(this.kvMap.entries,{ MutableBiDiEntry(it, this.vkMap)},{this.kvMap[it.key] = it.valuethis.vkMap[it.value] = it.keytrue},{this.kvMap.remove(it.key, it.value)this.vkMap.remove(it.value, it.key)true})override val keys: MutableSet<K>get() = ViewCollections.setMapped(this.kvMap.keys,{it},{false},{val value = this.kvMap.remove(it)this.vkMap.remove(value)true})override val values: MutableCollection<V>get() = ViewCollections.collectionMapped(this.kvMap.values,{it},{false},{val key = this.vkMap.remove(it)this.kvMap.remove(key)true})override val size: Intget() = this.kvMap.sizeoverride fun containsKey(key: K): Boolean = this.kvMap.containsKey(key)override fun containsValue(value: V): Boolean = this.kvMap.containsValue(value)override fun get(key: K): V? = this.kvMap[key]override fun isEmpty(): Boolean = this.kvMap.isEmpty()override fun clear() {this.kvMap.clear()this.vkMap.clear()}override fun remove(key: K): V? {val value = this.kvMap.remove(key)this.vkMap.remove(value)return value}override fun put(key: K, value: V): V? {val oldValue = this.kvMap.put(key, value)this.vkMap[value] = keyreturn oldValue}override fun putAll(from: Map<out K, V>) {for ((k, v) in from) {this[k] = v}}override val otherDirection: MutableBiDiMap<V, K> = OtherDirection()inner class OtherDirection: MutableBiDiMap<V, K> {override val entries: MutableSet<MutableMap.MutableEntry<V, K>>get() = ViewCollections.setMapped(this@MutableBiDiMapImpl.vkMap.entries,{ MutableBiDiEntry(it, this@MutableBiDiMapImpl.kvMap)},{this@MutableBiDiMapImpl.vkMap[it.key] = it.valuethis@MutableBiDiMapImpl.kvMap[it.value] = it.keytrue},{this@MutableBiDiMapImpl.vkMap.remove(it.key, it.value)this@MutableBiDiMapImpl.kvMap.remove(it.value, it.key)true})override val keys: MutableSet<V>get() = ViewCollections.setMapped(this@MutableBiDiMapImpl.vkMap.keys,{it},{false},{val value = this@MutableBiDiMapImpl.vkMap.remove(it)this@MutableBiDiMapImpl.kvMap.remove(value)true})override val values: MutableCollection<K>get() = ViewCollections.collectionMapped(this@MutableBiDiMapImpl.vkMap.values,{it},{false},{val key = this@MutableBiDiMapImpl.kvMap.remove(it)this@MutableBiDiMapImpl.vkMap.remove(key)true})override val size: Intget() = this@MutableBiDiMapImpl.vkMap.sizeoverride fun containsKey(key: V): Boolean = this@MutableBiDiMapImpl.vkMap.containsKey(key)override fun containsValue(value: K): Boolean = this@MutableBiDiMapImpl.vkMap.containsValue(value)override fun get(key: V): K? = this@MutableBiDiMapImpl.vkMap[key]override fun isEmpty(): Boolean = this@MutableBiDiMapImpl.vkMap.isEmpty()override val otherDirection: MutableBiDiMap<K, V>get() = this@MutableBiDiMapImploverride fun clear() {this@MutableBiDiMapImpl.kvMap.clear()this@MutableBiDiMapImpl.vkMap.clear()}override fun remove(key: V): K? {val value = this@MutableBiDiMapImpl.vkMap.remove(key)this@MutableBiDiMapImpl.kvMap.remove(value)return value}override fun put(key: V, value: K): K? {val oldValue = this@MutableBiDiMapImpl.vkMap.put(key, value)this@MutableBiDiMapImpl.kvMap[value] = keyreturn oldValue}override fun putAll(from: Map<out V, K>) {for ((k, v) in from) {this[k] = v}}}}class MutableBiDiEntry<K, V>(val entry: MutableMap.MutableEntry<K, V>, val vkMap: MutableMap<V, K>) : MutableMap.MutableEntry<K, V> {override val key: Kget() = entry.keyoverride val value: Vget() = entry.valueoverride fun setValue(newValue: V): V {this.vkMap[newValue] = this.keyreturn entry.setValue(newValue)}}fun <K, V> Map<K, V>.createVKMap(): Map<V, K> {val map = mutableMapOf<V, K>()this.entries.forEach {map[it.value] = it.key}return map}fun <K, V> MutableMap<K, V>.createVKMutableMap(): MutableMap<V, K> {val map = mutableMapOf<V, K>()this.entries.forEach {map[it.value] = it.key}return map}fun <K, V> mutableBiDiMap(vararg pairs: Pair<K, V>): MutableBiDiMap<K, V> =MutableBiDiMapImpl(mutableMapOf(*pairs),mutableMapOf(*pairs.inverted()),)fun <K, V> biDiMap(vararg pairs: Pair<K, V>): BiDiMap<K, V> =BiDiMapImpl(mutableMapOf(*pairs),mutableMapOf(*pairs.inverted()),)fun <K, V> Array<out Pair<K, V>>.inverted(): Array<Pair<V, K>> =Array(this.size) {this[it].second to this[it].first}
package com.github.jonathanxd.dracon.revisiondata class ChannelRevision(val channel: String, val revision: PijulRevisionNumber)data class ChannelRevisions(val revisionsByChannel: List<ChannelRevision>)
package com.github.jonathanxd.dracon.repositoryimport com.github.jonathanxd.dracon.channel.PijulChannelManagerimport com.github.jonathanxd.dracon.channel.PijulChannelTypeimport com.github.jonathanxd.dracon.i18n.DraconBundleimport com.github.jonathanxd.dracon.util.BiDiMapimport com.github.jonathanxd.dracon.util.biDiMapimport com.intellij.openapi.components.serviceimport com.intellij.openapi.project.Projectimport com.intellij.vcs.log.*import com.intellij.vcs.log.impl.SimpleRefGroupimport com.intellij.vcs.log.impl.SimpleRefTypeimport java.io.DataInputimport java.io.DataOutputclass PijulVcsLogRefManager(val project: Project) : VcsLogRefManager {companion object {val CURRENT: VcsRefType = SimpleRefType("CURRENT", false, VcsLogStandardColors.Refs.TIP)val CHANNEL: VcsRefType = SimpleRefType("CHANNEL", true, VcsLogStandardColors.Refs.BRANCH)val TAG: VcsRefType = SimpleRefType("TAG", false, VcsLogStandardColors.Refs.TAG)val OTHER: VcsRefType = SimpleRefType("OTHER", true, VcsLogStandardColors.Refs.TAG)private val REF_TYPE_INDEX: BiDiMap<VcsRefType, Int> = biDiMap(CURRENT to 0,CHANNEL to 1,TAG to 2,OTHER to 100)}private val labelSorter = Comparator<VcsRef> { l, r ->REF_TYPE_INDEX[l.type]!!.compareTo(REF_TYPE_INDEX[r.type]!!)}private val channelSorter = Comparator<VcsRef> { l, r ->REF_TYPE_INDEX[l.type]!!.compareTo(REF_TYPE_INDEX[r.type]!!)}val channelManager = project.service<PijulChannelManager>()val repositoryManager = project.service<PijulRepositoryManager>()override fun getBranchLayoutComparator(): Comparator<VcsRef> = this.channelSorteroverride fun getLabelsOrderComparator(): Comparator<VcsRef> = this.labelSorteroverride fun groupForBranchFilter(refs: MutableCollection<out VcsRef>): MutableList<RefGroup> {val current = mutableListOf<VcsRef>()val channels = mutableListOf<VcsRef>()val tags = mutableListOf<VcsRef>()val others = mutableListOf<VcsRef>()for (ref in refs) {when (ref.type) {CURRENT -> {current.add(ref)}CHANNEL -> {channels.add(ref)}TAG -> {tags.add(ref)}else -> {others.add(ref)}}}val groups = mutableListOf<RefGroup>()if (current.isNotEmpty()) {groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.current"), current.toList()))}if (channels.isNotEmpty()) {groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.channel"), channels.toList()))}if (tags.isNotEmpty()) {groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.tag"), tags.toList()))}if (others.isNotEmpty()) {groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.other"), others.toList()))}return groups}override fun groupForTable(refs: MutableCollection<out VcsRef>,compact: Boolean,showTagNames: Boolean): MutableList<RefGroup> {// TODO: Support compact and showTagNamesval mainChannels = mutableListOf<VcsRef>()val channels = mutableListOf<VcsRef>()val tags = mutableListOf<VcsRef>()val others = mutableListOf<VcsRef>()for (ref in refs) {when (ref.type) {CURRENT -> {}CHANNEL -> {if (ref.name == "main") {mainChannels.add(ref)} else {channels.add(ref)}}TAG -> {tags.add(ref)}else -> {others.add(ref)}}}val hasCurrent = refs.any { it.type == CURRENT }val groups = mutableListOf<RefGroup>()if (mainChannels.isNotEmpty() && hasCurrent) {groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.mainChannel"), mainChannels.toList()))}if (channels.isNotEmpty()) {groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.channel"), channels.toList()))}if (tags.isNotEmpty()) {val f = tags.first()groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.tag_text", f.name), tags.toList()))}if (others.isNotEmpty()) {groups.add(SimpleRefGroup(DraconBundle.message("pijul.log.refGroup.other"), others.toList()))}return groups}override fun serialize(out: DataOutput, type: VcsRefType) {out.writeInt(REF_TYPE_INDEX[type]!!)}override fun deserialize(input: DataInput): VcsRefType {val id = input.readInt()return REF_TYPE_INDEX.otherDirection[id]!!}override fun isFavorite(reference: VcsRef): Boolean =when (reference.type) {CURRENT -> trueCHANNEL -> this.channelManager.isFavorite(PijulChannelType.CHANNEL,this.repositoryManager.getRepositoryForFileQuick(reference.root),reference.name)else -> false}override fun setFavorite(reference: VcsRef, favorite: Boolean) {when (reference.type) {CURRENT -> returnCHANNEL -> this.channelManager.setFavorite(PijulChannelType.CHANNEL,this.repositoryManager.getRepositoryForFileQuick(reference.root),reference.name,favorite)else -> return}}}
package com.github.jonathanxd.dracon.repository@FunctionalInterfaceinterface PijulRepositoryChangeListener {fun repositoryChanged(repository: PijulRepository)}fun PijulRepositoryChangeListener(f: (repository: PijulRepository) -> Unit): PijulRepositoryChangeListener =object : PijulRepositoryChangeListener {override fun repositoryChanged(repository: PijulRepository) {f(repository)}}
package com.github.jonathanxd.dracon.logimport com.github.jonathanxd.dracon.push.parseDateimport org.tomlj.Tomlimport java.time.ZonedDateTimeimport java.util.*data class PijulTag(val hash: String,val authors: List<Author>,val date: ZonedDateTime,val state: String,val message: String)val PijulTag.simpleMessage get() =if (this.message.isEmpty()) ""else this.message.split("\n").first { it.trim().isNotEmpty() }.trim()val AUTHOR_REGEX = Regex("Author \\{ name: (\"([^\"]+)\"|None), full_name: (Some\\(\"([^\"]+)\"\\)|None), email: (Some\\(\"([^\"]+)\"\\)|None) }")fun String.parseTags(): List<PijulTag> {if (this.isEmpty()) return emptyList()val lines = this.lines().listIterator()val tags = mutableListOf<PijulTag>()while (lines.hasNext()) {var hash: String? = nullval authors = mutableListOf<Author>()var date: ZonedDateTime? = nullvar state: String? = nullval message = StringJoiner("\n")var messageStarted = falsewhile (lines.hasNext()) {val line = lines.next()if (line.startsWith("Tag")) {hash = line.substring("Tag ".length)} else if (line.startsWith("Author: ")) {val finds = AUTHOR_REGEX.findAll(line)for (find in finds) {val groups = find.groupsval name: String? = groups[2]?.valueval fullName: String? = groups[4]?.valueval email: String? = groups[6]?.valueauthors += Author(name, fullName, email)}} else if (line.startsWith("Date: ")) {val dateString = line.substring("Date: ".length)date = dateString.parseDate()} else if (line.startsWith("State: ")) {state = line.substring("State: ".length)} else {if (line.startsWith("Tag")) {lines.previous()break;} else if (line.trim().isEmpty() && state != null && !messageStarted) {messageStarted = true} else {if (line.startsWith(" ")) {message.add(line.substring(" ".length))} else {message.add(line)}}}}tags += PijulTag(hash!!, authors, date!!, state!!, message.toString().removeTrailingLineJump())}return tags}private val TRAILING_LINE_JUMP = Regex("[\n]+$")fun String.removeTrailingLineJump() =this.replace(TRAILING_LINE_JUMP, "")
package com.github.jonathanxd.dracon.logimport com.intellij.openapi.project.Projectimport com.intellij.openapi.vcs.changes.Changeimport com.intellij.openapi.vfs.VirtualFileimport com.intellij.vcs.log.VcsShortCommitDetailsimport com.intellij.vcs.log.impl.VcsChangesLazilyParsedDetailsimport com.intellij.vcs.log.impl.VcsChangesLazilyParsedDetails.ChangesParserimport com.intellij.vcs.log.impl.VcsFileStatusInfoimport java.util.*class PijulRecordDetails(val project: Project,val rootFile: VirtualFile,val entry: PijulLogEntryCommitDetails) : VcsChangesLazilyParsedDetails(project, entry.id, emptyList(), entry.commitTime, rootFile, entry.subject, entry.author,entry.fullMessage, entry.committer, entry.authorTime,(listOf(PijulHash(entry.entry.changeHash)) /*+ entry.getPijulParents()*/).map {entry.changeRetriever(it.hash).map {VcsFileStatusInfo(it.type,(it.beforeRevision?.file?.path ?: it.afterRevision?.file?.path)!!,it.afterRevision?.file?.path)}},PijulChangesParser(entry))private class PijulChangesParser(val entry: PijulLogEntryCommitDetails) : ChangesParser {override fun parseStatusInfo(project: Project,commit: VcsShortCommitDetails,changes: List<VcsFileStatusInfo>,parentIndex: Int): List<Change> {var parentHash: String? = null/*if (parentIndex < commit.parents.size) {parentHash = commit.parents[parentIndex].asString()}*/return entry.changeRetriever(parentHash ?: entry.entry.changeHash)}}
package com.github.jonathanxd.dracon.logimport com.github.jonathanxd.dracon.PijulVcsimport com.github.jonathanxd.dracon.changes.PijulCommittedChangeListimport com.github.jonathanxd.dracon.pijul.Pijulimport com.github.jonathanxd.dracon.pijul.pijulimport com.github.jonathanxd.dracon.repository.PijulRepositoryimport com.github.jonathanxd.dracon.repository.PijulRepositoryChangeListenerimport com.github.jonathanxd.dracon.repository.PijulVcsLogRefManagerimport com.github.jonathanxd.dracon.util.convertToNioimport com.github.jonathanxd.dracon.util.pijulimport com.intellij.openapi.Disposableimport com.intellij.openapi.application.ReadActionimport com.intellij.openapi.project.Projectimport com.intellij.openapi.vcs.FilePathimport com.intellij.openapi.vcs.VcsKeyimport com.intellij.openapi.vfs.VirtualFileimport com.intellij.util.Consumerimport com.intellij.util.messages.MessageBusConnectionimport com.intellij.vcs.log.*import com.intellij.vcs.log.impl.HashImplimport com.intellij.vcs.log.impl.LogDataImplimport com.intellij.vcs.log.impl.VcsUserImplimport org.apache.commons.codec.digest.DigestUtilsimport java.time.ZoneIdclass PijulLogProvider(val project: Project) : VcsLogProvider {val manager = PijulVcsLogRefManager(project)override fun readFirstBlock(root: VirtualFile,requirements: VcsLogProvider.Requirements): VcsLogProvider.DetailedLogData {return createMetadata(this.project, root) {this.project.pijul().log(this.project, root.convertToNio(), it).result?.entries.orEmpty()}}override fun readAllHashes(root: VirtualFile, commitConsumer: Consumer<in TimedVcsCommit>): VcsLogProvider.LogData {return createMetadata(this.project, root, {this.project.pijul().log(this.project, root.convertToNio(), it).result?.entries.orEmpty()}, { commitConsumer.consume(it) })}override fun readMetadata(root: VirtualFile,hashes: MutableList<String>,consumer: Consumer<in VcsCommitMetadata>) {createMetadata(this.project, root, {this.project.pijul().log(this.project, root.convertToNio(), it).result?.entries.orEmpty().filter {hashes.contains(DigestUtils.sha1Hex(it.changeHash))}}, {consumer.consume(it)})}override fun readFullDetails(root: VirtualFile,hashes: MutableList<String>,commitConsumer: Consumer<in VcsFullCommitDetails>) {createMetadataForEntries(this.project, root, {this.project.pijul().log(this.project, root.convertToNio(), it).result?.entries.orEmpty().filter {hashes.contains(DigestUtils.sha1Hex(it.changeHash))}}, { _, it ->commitConsumer.consume(PijulRecordDetails(this.project, root, it))})}override fun getSupportedVcs(): VcsKey {return PijulVcs.KEY}override fun getReferenceManager(): VcsLogRefManager =this.manageroverride fun subscribeToRootRefreshEvents(roots: MutableCollection<out VirtualFile>,refresher: VcsLogRefresher): Disposable {val connection: MessageBusConnection = this.project.messageBus.connect()connection.subscribe(PijulRepository.PIJUL_REPO_CHANGE, PijulRepositoryChangeListener { repository ->val root: VirtualFile = repository.rootif (roots.contains(root)) {refresher.refresh(root)}})return connection}override fun getCommitsMatchingFilter(root: VirtualFile,filterCollection: VcsLogFilterCollection,maxCount: Int): MutableList<TimedVcsCommit> {val rootPath = root.convertToNio()val channelFilters = mutableListOf<(String) -> Boolean>()val filters = mutableListOf<(PijulLogEntry) -> Boolean>()val fullDetailsFilters = mutableListOf<(VcsFullCommitDetails) -> Boolean>()val metadataFilters = mutableListOf<(VcsCommitMetadata) -> Boolean>()val date = filterCollection.get(VcsLogFilterCollection.DATE_FILTER)date?.after?.let { after ->filters.add {it.date.isAfter(after.toInstant().atZone(ZoneId.of("UTC")))}}date?.before?.let { before ->filters.add {it.date.isBefore(before.toInstant().atZone(ZoneId.of("UTC")))}}filterCollection.get(VcsLogFilterCollection.BRANCH_FILTER)?.let { filter ->channelFilters.add {filter.matches(it)}}filterCollection.get(VcsLogFilterCollection.REVISION_FILTER)?.let { filter ->filters.add {filter.heads.any { head ->it.asSha1Hash == head.hash.asString()}}}filterCollection.get(VcsLogFilterCollection.USER_FILTER)?.let { filter ->metadataFilters.add {filter.matches(it)}}filterCollection.get(VcsLogFilterCollection.HASH_FILTER)?.let { filter ->filters.add {filter.hashes.any { hash ->it.changeHash == hash || it.asSha1Hash == hash}}}filterCollection.get(VcsLogFilterCollection.TEXT_FILTER)?.let { filter ->filters.add {filter.matches(it.message)}}filterCollection.get(VcsLogFilterCollection.STRUCTURE_FILTER)?.let { filter ->filters.add {filter.files.any { filterRoot ->val filterRootPath = filterRoot.convertToNio()it.hunks.any { hunk ->(hunk is HunkWithPath) && hunk.resolvePath(rootPath).startsWith(filterRootPath)}}}}filterCollection.get(VcsLogFilterCollection.ROOT_FILTER)?.let { filter ->filters.add {filter.roots.any { filterRoot ->val filterRootPath = filterRoot.convertToNio()it.hunks.any { hunk ->(hunk is HunkWithPath) && hunk.resolvePath(rootPath).startsWith(filterRootPath)}}}}val records = mutableListOf<TimedVcsCommit>()createMetadataForEntries(this.project, root, {if (channelFilters.all { filter -> filter(it) }) {this.project.pijul().log(this.project, root.convertToNio(), it).result?.entries.orEmpty().filter { filters.all { filter -> filter(it) } }} else {emptyList()}}, { _, it ->if (fullDetailsFilters.all { filter -> filter(it) }) {val meta = createMetadata(project, root, it)if (metadataFilters.all { filter -> filter(meta) }) {records += meta}}})return records}override fun getCurrentUser(root: VirtualFile): VcsUser? {return null}override fun getContainingBranches(root: VirtualFile, commitHash: Hash): MutableCollection<String> {val channels = mutableListOf<String>()createMetadataForEntries(this.project, root, {this.project.pijul().log(this.project, root.convertToNio(), it).result?.entries.orEmpty().filter {DigestUtils.sha1Hex(it.changeHash) == commitHash.asString()}}, { c, it ->channels += c})return channels}override fun <T : Any?> getPropertyValue(property: VcsLogProperties.VcsLogProperty<T>?): T? {if (property == VcsLogProperties.HAS_COMMITTER) {return true as T}return null}override fun getVcsRoot(project: Project, detectedRoot: VirtualFile, filePath: FilePath): VirtualFile? {return Pijul.findPijulDirectory(detectedRoot)}override fun getCurrentBranch(root: VirtualFile): String? {return this.project.pijul().channel(this.project, root).result?.channels?.firstOrNull { it.current }?.name}companion object {fun createMetadata(project: Project,root: VirtualFile,fetcher: (channel: String) -> List<PijulLogEntry>): VcsLogProvider.DetailedLogData {val commits = mutableListOf<VcsCommitMetadata>()val log = createMetadata(project, root, fetcher) {commits.add(it)}return LogDataImpl(log.refs, commits)}fun createMetadataForEntries(project: Project,root: VirtualFile,fetcher: (channel: String) -> List<PijulLogEntry>,consumer: (channel: String, PijulLogEntryCommitDetails) -> Unit): VcsLogProvider.LogData {val latest = project.pijul().latestRevisionNumber(project, root.convertToNio()).result?.hashval allChannels = project.pijul().channel(project, root.convertToNio()).result?.channels.orEmpty()val currentChannel = allChannels.firstOrNull {it.current}val refs = mutableSetOf<VcsRef>()val authors = mutableSetOf<VcsUser>()val rootPath = root.convertToNio()val factory = getObjectsFactoryWithDisposeCheck(project) ?: return LogDataImpl.empty()allChannels.forEach { channel ->val allTags = if (channel.current) {// Only current tag of current channel is available through `pijul tag` ATMproject.pijul().tags(project, root.convertToNio()).result.orEmpty()} else {emptyList()}allTags.forEach {val hash = HashImpl.build(DigestUtils.sha1Hex(it.hash))//PijulHash(it.hash)val ref = factory.createRef(hash, it.simpleMessage, PijulVcsLogRefManager.TAG, root)if (!refs.contains(ref)) refs.add(ref)}var first = truefetcher(channel.name).forEach {val hash = HashImpl.build(DigestUtils.sha1Hex(it.changeHash))//PijulHash(it.changeHash)authors.addAll(it.authors.filter { it.name != null && it.email != null }.map { VcsUserImpl(it.name!!, it.email!!) })if (channel.current) {// Only current tag of current channel is available through `pijul tag` ATMval tag = allTags.singleOrNull()if (tag != null) {if (first) {val ref = factory.createRef(hash, tag.simpleMessage, PijulVcsLogRefManager.TAG, root)if (!refs.contains(ref)) refs.add(ref)}}}first = falseif (it.changeHash == latest) {val ref = factory.createRef(hash, "CURRENT", PijulVcsLogRefManager.CURRENT, root)if (!refs.contains(ref)) refs.add(ref)}val ref = factory.createRef(hash, channel.name, PijulVcsLogRefManager.CHANNEL, root)if (!refs.contains(ref)) refs.add(ref)val details = PijulLogEntryCommitDetails(rootPath,project,it,) {val changes = mutableListOf<PijulCommittedChangeList>()pijul(project).changes(project, rootPath, it, 1, {true}) {changes += it}changes.single().changes.toList()}consumer(channel.name, details)}}return LogDataImpl(refs, authors)}fun createMetadata(project: Project,root: VirtualFile,fetcher: (channel: String) -> List<PijulLogEntry>,consumer: (VcsCommitMetadata) -> Unit): VcsLogProvider.LogData {val factory = getObjectsFactoryWithDisposeCheck(project) ?: return LogDataImpl.empty()return createMetadataForEntries(project, root, fetcher) { _, it ->consumer(factory.createCommitMetadata(it.id,emptyList(),it.commitTime,root,it.subject,it.author.name,it.author.email,it.fullMessage,it.committer.name,it.committer.email,it.authorTime))}}fun createMetadata(project: Project,root: VirtualFile,it: PijulLogEntryCommitDetails): VcsCommitMetadata {val factory = getObjectsFactoryWithDisposeCheck(project)!!return factory.createCommitMetadata(it.id,emptyList(),it.commitTime,root,it.subject,it.author.name,it.author.email,it.fullMessage,it.committer.name,it.committer.email,it.authorTime)}fun getObjectsFactoryWithDisposeCheck(project: Project): VcsLogObjectsFactory? {return ReadAction.compute<VcsLogObjectsFactory?, RuntimeException> {if (!project.isDisposed) {return@compute project.getService(VcsLogObjectsFactory::class.java)}null}}}}
package com.github.jonathanxd.dracon.logimport com.intellij.openapi.project.Projectimport com.intellij.openapi.vcs.changes.Changeimport com.intellij.openapi.vfs.VirtualFileimport com.intellij.openapi.vfs.VirtualFileManagerimport com.intellij.vcs.log.Hashimport com.intellij.vcs.log.VcsFullCommitDetailsimport com.intellij.vcs.log.VcsUserimport com.intellij.vcs.log.impl.HashImplimport com.intellij.vcs.log.impl.VcsUserImplimport org.apache.commons.codec.digest.DigestUtilsimport java.nio.file.Pathclass PijulLogEntryCommitDetails(val root: Path,val project: Project,val entry: PijulLogEntry,val changeRetriever: (String) -> List<Change>) : VcsFullCommitDetails {override fun getId(): Hash =HashImpl.build(DigestUtils.sha1Hex(this.entry.changeHash))//PijulHash(this.entry.changeHash)override fun getParents(): MutableList<Hash> =//this.entry.dependencies.map { it.hash }.map(::PijulHash).toMutableList()this.entry.dependencies.map { it.hash }.map { DigestUtils.sha1Hex(it) }.map { HashImpl.build(it) }.toMutableList()fun getPijulParents(): List<PijulHash> =this.entry.dependencies.map { it.hash }.map(::PijulHash)override fun getTimestamp(): Long = this.entry.date.toInstant().toEpochMilli()override fun getRoot(): VirtualFile =VirtualFileManager.getInstance().findFileByNioPath(this.root)!!override fun getSubject(): String =if (this.entry.message.isEmpty()) ""else this.entry.message.split("\n").first { it.trim().isNotEmpty() }override fun getAuthor(): VcsUser =this.entry.authors.firstOrNull()?.let {VcsUserImpl(it.name ?: "", it.email ?: "")} ?: VcsUserImpl("", "")override fun getCommitter(): VcsUser =this.authoroverride fun getAuthorTime(): Long =this.timestampoverride fun getCommitTime(): Long =this.timestampoverride fun getFullMessage(): String =this.entry.messageoverride fun getChanges(): MutableCollection<Change> =this.changeRetriever(this.entry.changeHash).toMutableList()override fun getChanges(parent: Int): MutableCollection<Change> =this.changeRetriever(this.parents[parent].asString()).toMutableList()}
val RFC3339_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSSSSSSSS]XXX")
val RFC3339_FORMATTER = DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd'T'HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).appendOffsetId().toFormatter()
}}fun isIgnored(root: Path, file: Path): Boolean {val ignore = root.resolve(".ignore")return if (Files.exists(ignore) && Files.isRegularFile(ignore)) {val lines = Files.readAllLines(ignore, Charsets.UTF_8)val paths = lines.map { path ->root.resolve(path)}paths.any { path -> file.startsWith(path) }} else {false
override fun tags(project: Project, root: Path): PijulOperationResult<List<PijulTag>> {val result = this.doExecutionWithMapperEvenFailed("tag", this.createPainlessExecPijulOperation(project, root, listOf("tag"))) {it.parseTags()}if (result.result != null && result.result.isNotEmpty()) { // Force success because Pijul is failing with `Not a directory` even in success case.return PijulOperationResult("tag", SuccessStatusCode, result.result)} else {return result}}
override fun log(project: Project, root: Path, channel: String): PijulOperationResult<PijulLog> {val logHashExecution = this.createPainlessExecPijulOperation(project, root, listOf("log", "--hash-only", "--channel", channel))val hashes = this.doExecutionWithMapper("log--hash-only", logHashExecution) {it.lines()}
if (hashes.statusCode !is SuccessStatusCode) {return hashes as PijulOperationResult<PijulLog>} else {val entries = mutableListOf<PijulLogEntry>()for (hash in hashes.result!!) {if (hash.isEmpty()) {break}val change = this.pijulLogEntryCache.load(hash) {this.doExecutionWithMapper("change-$hash",this.createPainlessExecPijulOperation(project, root, listOf("change", hash))) {it.parseChange(hash)}}if (change.statusCode !is SuccessStatusCode) {return change as PijulOperationResult<PijulLog>} else if (change.result != null) {entries += change.result}}return PijulOperationResult(hashes.operation, hashes.statusCode, PijulLog(entries))}}
override fun revisionByChannel(project: Project, root: Path): PijulOperationResult<ChannelRevisions> {return this.channel(project, root).flatMap {val mapped = it.channels.map { ch ->this.log(project, root, ch.name).map {it.entries.firstOrNull()?.let {ChannelRevision(ch.name, PijulRevisionNumber(it.changeHash, it.date))}}}for (m in mapped) {if (m.result == null)return@flatMap m as PijulOperationResult<ChannelRevisions>}PijulOperationResult("revision by channel",SuccessStatusCode,ChannelRevisions(mapped.map { it.result!! }))}}
else ChannelInfo(channels.filter { it.isNotEmpty() }.map { PijulChannel(it[0] == '*', it.substring(1)) })
else ChannelInfo(channels.filter { it.isNotEmpty() }.map {val name = it.substring(1)val effectiveName = if (name.startsWith(" ")) name.substring(1) else namePijulChannel(it[0] == '*', effectiveName)})
package com.github.jonathanxd.dracon.channelimport com.intellij.dvcs.branch.BranchTypeenum class PijulChannelType(private val nname: String) : BranchType {CHANNEL("CHANNEL");override fun getName(): String = this.nname}
package com.github.jonathanxd.dracon.channelimport com.github.jonathanxd.dracon.config.PijulSettingsimport com.intellij.dvcs.branch.BranchTypeimport com.intellij.dvcs.branch.DvcsBranchManagerimport com.intellij.openapi.components.Serviceimport com.intellij.openapi.project.Project@Serviceclass PijulChannelManager(val project: Project) : DvcsBranchManager(project,PijulSettings.instance.getChannelSettings(),PijulChannelType.values()) {override fun getDefaultBranchName(type: BranchType): String? =when (type) {PijulChannelType.CHANNEL -> "main"else -> null}}