commit - f3ca72ddd01412bbbba872218e1fdf805b1cf72d
commit + a3f6382b95b1085b8c2ba724827d033669b9b5b8
blob - /dev/null
blob + 6d217996761e5c7534801c216d48eb1172ee761a (mode 644)
--- /dev/null
+++ .kotlin/errors/errors-1784879603938.log
+kotlin version: 2.4.10
+error message: Daemon compilation failed
+
blob - 71cbd06edba31dd52ad4b0b3547d8f20376ce481
blob + 25846216f6513e9519a5fe1eabcc603d60f9f99a
--- src/main/kotlin/dev/nezzontli/gotvcs/checkin/GotCheckinEnvironment.kt
+++ src/main/kotlin/dev/nezzontli/gotvcs/checkin/GotCheckinEnvironment.kt
feedback: MutableSet<in String>,
): MutableList<VcsException> = doCommit(changes, commitMessage)
- private fun doCommit(changes: List<Change>, commitMessage: String): MutableList<VcsException> {
+ private fun doCommit(changes: List<Change>, commitMessage: String): MutableList<VcsException> =
+ commitFilePaths(changes.mapNotNull { it.afterRevision?.file ?: it.beforeRevision?.file }, commitMessage)
+
+ /**
+ * Exposed for GotCommitAndSendExecutor: unlike the default "Commit"
+ * button (a VCS_COMMIT session, for which the platform automatically
+ * calls scheduleUnversionedFilesForAddition()/scheduleMissingFileForDeletion()
+ * before committing), a custom CommitExecutor's session is on its own
+ * for that -- confirmed by decompiling ChangesViewCommitWorkflowHandler,
+ * whose addUnversionedFiles() is a no-op unless CommitSessionInfo.isVcsCommit()
+ * is true. This lets that caller commit an explicit path list (tracked
+ * changes plus whatever it scheduled for add/deletion itself) in one go.
+ */
+ fun commitFilePaths(filePaths: List<FilePath>, commitMessage: String): MutableList<VcsException> {
val exceptions = mutableListOf<VcsException>()
- val filePaths = changes.mapNotNull { it.afterRevision?.file ?: it.beforeRevision?.file }
for ((workDir, paths) in groupByRoot(filePaths)) {
try {
commandLine.commit(workDir, commitMessage, paths)
blob - /dev/null
blob + 00dd2062abec9b198fe10c55daef4490c8bc8ce1 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/checkin/GotCommitAndSendExecutor.kt
+package dev.nezzontli.gotvcs.checkin
+
+import com.intellij.notification.NotificationGroupManager
+import com.intellij.notification.NotificationType
+import com.intellij.openapi.actionSystem.ActionManager
+import com.intellij.openapi.actionSystem.ActionUiKind
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.actionSystem.Presentation
+import com.intellij.openapi.actionSystem.ex.ActionUtil
+import com.intellij.openapi.actionSystem.impl.SimpleDataContext
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.ProjectLevelVcsManager
+import com.intellij.openapi.vcs.changes.Change
+import com.intellij.openapi.vcs.changes.ChangeListManager
+import com.intellij.openapi.vcs.changes.ChangeListManagerImpl
+import com.intellij.openapi.vcs.changes.CommitContext
+import com.intellij.openapi.vcs.changes.CommitExecutor
+import com.intellij.openapi.vcs.changes.CommitSession
+import com.intellij.vcsUtil.VcsUtil
+import dev.nezzontli.gotvcs.repo.GotRepositoryManager
+
+const val COMMIT_AND_SEND_EXECUTOR_ID = "Got.Commit.And.Send"
+
+/**
+ * Commits exactly like the default "Commit" button (delegates to the same
+ * CheckinEnvironment), then opens the native Push dialog (same action as
+ * Ctrl+Shift+K) so the user can review and send from there.
+ */
+class GotCommitAndSendExecutor(
+ private val project: Project,
+ private val checkinEnvironment: GotCheckinEnvironment,
+) : CommitExecutor {
+
+ override fun getActionText(): String = "Commit and Send"
+
+ override fun getId(): String = COMMIT_AND_SEND_EXECUTOR_ID
+
+ override fun createCommitSession(context: CommitContext): CommitSession = session
+
+ private val session = object : CommitSession {
+ // execute() runs on a background thread (same as the default commit
+ // action), but opening the Push dialog is a UI action and must
+ // happen on the EDT -- see com.intellij.execution.process
+ // .OSProcessHandler#checkEdtAndReadAction-style threading rules.
+ override fun execute(changes: MutableCollection<out Change>, commitMessage: String?) {
+ val message = commitMessage.orEmpty()
+
+ // Unlike the default "Commit" button, a custom CommitExecutor's
+ // session does NOT get scheduleUnversionedFilesForAddition() /
+ // scheduleMissingFileForDeletion() called automatically by the
+ // platform first (verified by decompiling
+ // ChangesViewCommitWorkflowHandler.addUnversionedFiles(), which
+ // is a no-op unless CommitSessionInfo.isVcsCommit() is true) --
+ // and `got commit -m msg <path>` errors with "no changes to
+ // commit" for a path that hasn't been `got add`-ed yet (verified
+ // directly against the CLI). So new/deleted files must be
+ // staged here ourselves before the actual commit.
+ val changeListManager = ChangeListManager.getInstance(project) as ChangeListManagerImpl
+ val unversionedFiles = changeListManager.unversionedFilesPaths.mapNotNull { it.virtualFile }
+ val deletedPaths = changeListManager.deletedFiles.map { it.path }
+
+ if (unversionedFiles.isNotEmpty()) {
+ checkinEnvironment.scheduleUnversionedFilesForAddition(unversionedFiles.toMutableList())
+ }
+ if (deletedPaths.isNotEmpty()) {
+ checkinEnvironment.scheduleMissingFileForDeletion(deletedPaths.toMutableList())
+ }
+
+ val trackedPaths = changes.mapNotNull { it.afterRevision?.file ?: it.beforeRevision?.file }
+ val unversionedFilePaths = unversionedFiles.map { VcsUtil.getFilePath(it) }
+ val allPaths: List<FilePath> = (trackedPaths + unversionedFilePaths + deletedPaths).distinct()
+
+ val errors = checkinEnvironment.commitFilePaths(allPaths, message)
+ if (errors.isNotEmpty()) {
+ val group = NotificationGroupManager.getInstance().getNotificationGroup("got")
+ group.createNotification(
+ "got commit failed",
+ errors.joinToString("\n") { it.message.orEmpty() },
+ NotificationType.ERROR,
+ ).notify(project)
+ return
+ }
+
+ // GotRepository's cached branch/revision (used to resolve the
+ // Push dialog's target) is normally refreshed by the platform's
+ // own post-commit VCS-root-dirty machinery, but that happens
+ // asynchronously and can race with the dialog opening right
+ // below -- refreshing it here ourselves, still on this
+ // background thread, guarantees the dialog sees the commit that
+ // was just made instead of possibly-stale cached state.
+ val vcsManager = ProjectLevelVcsManager.getInstance(project)
+ val repositoryManager = project.getService(GotRepositoryManager::class.java)
+ val affectedRoots = allPaths.mapNotNull { vcsManager.getVcsRootFor(it) }.toSet()
+ for (root in affectedRoots) {
+ repositoryManager.getRepositoryForRoot(root)?.update()
+ }
+
+ ApplicationManager.getApplication().invokeLater {
+ val pushAction = ActionManager.getInstance().getAction("Vcs.Push") ?: return@invokeLater
+ val dataContext = SimpleDataContext.getProjectContext(project)
+ val event = AnActionEvent.createEvent(
+ dataContext,
+ Presentation(),
+ "GotCommitAndSend",
+ ActionUiKind.NONE,
+ null,
+ )
+ ActionUtil.performAction(pushAction, event)
+ }
+ }
+ }
+}
blob - /dev/null
blob + 304c9f39bf6f17b49d46dbe001c9067492d285bd (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/checkin/GotCommitAndSendExecutorAction.kt
+package dev.nezzontli.gotvcs.checkin
+
+import com.intellij.openapi.vcs.changes.actions.BaseCommitExecutorAction
+
+/** Renders as the secondary (gray) toolbar button, next to the primary blue "Commit" button. */
+class GotCommitAndSendExecutorAction : BaseCommitExecutorAction() {
+ override val executorId: String = COMMIT_AND_SEND_EXECUTOR_ID
+}
blob - /dev/null
blob + 4cd0edd93bd88e55d33348212bc95b0946dc5b72 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/log/GotCommitContentRevision.kt
+package dev.nezzontli.gotvcs.log
+
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.VcsException
+import com.intellij.openapi.vcs.changes.ContentRevision
+import com.intellij.openapi.vcs.history.VcsRevisionNumber
+import dev.nezzontli.gotvcs.changes.GotRevisionNumber
+import dev.nezzontli.gotvcs.cli.GotCommandLineWrapper
+import java.io.File
+
+/**
+ * File content at an arbitrary historical commit (`got cat -c <commit> -P`),
+ * for the Log tab's per-commit diff. Unlike GotContentRevision (which is
+ * pinned to the work tree's current base commit), this one is parametrized
+ * by whichever commit the Log tab is showing -- called from background log
+ * loading, never from EDT-bound UI rendering, so it may shell out lazily.
+ */
+class GotCommitContentRevision(
+ private val filePath: FilePath,
+ private val workDir: File,
+ private val relativePath: String,
+ private val commandLine: GotCommandLineWrapper,
+ private val commitId: String,
+) : ContentRevision {
+
+ @Throws(VcsException::class)
+ override fun getContent(): String = commandLine.catAt(workDir, commitId, relativePath)
+
+ override fun getFile(): FilePath = filePath
+
+ override fun getRevisionNumber(): VcsRevisionNumber = GotRevisionNumber(commitId)
+}
blob - /dev/null
blob + d283e140140370e12759cba3fbc196bfea092aab (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/log/GotFullCommitDetails.kt
+package dev.nezzontli.gotvcs.log
+
+import com.intellij.openapi.vcs.changes.Change
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.vcs.log.Hash
+import com.intellij.vcs.log.VcsFullCommitDetails
+import com.intellij.vcs.log.VcsUser
+import com.intellij.vcs.log.impl.HashImpl
+import com.intellij.vcs.log.util.VcsUserUtil
+import dev.nezzontli.gotvcs.cli.GotCommitObject
+
+/**
+ * Adapts a [GotCommitObject] (parsed from `got cat <commit-id>`) to the
+ * platform's commit-details model, shared by the Push dialog's
+ * outgoing-commits preview (no [changes] needed there) and the Log tab
+ * (which populates [changes] from `got log -P`). [getChanges] returns the
+ * same flat list regardless of [parent] index -- got's `-P` output diffs
+ * against the immediate history parent, so this isn't fully merge-aware
+ * for commits with more than one parent, but is correct for the common case.
+ */
+class GotFullCommitDetails(
+ private val commitObject: GotCommitObject,
+ private val root: VirtualFile,
+ private val changes: List<Change> = emptyList(),
+) : VcsFullCommitDetails {
+
+ private val hash: Hash = HashImpl.build(commitObject.commitId)
+ private val author: VcsUser = VcsUserUtil.createUser(commitObject.authorName, commitObject.authorEmail)
+
+ override fun getId(): Hash = hash
+ override fun getParents(): List<Hash> = commitObject.parents.map { HashImpl.build(it) }
+ override fun getTimestamp(): Long = commitObject.authorTimestamp * 1000L
+ override fun getRoot(): VirtualFile = root
+ override fun getSubject(): String = commitObject.message.lineSequence().firstOrNull().orEmpty()
+ override fun getAuthor(): VcsUser = author
+ override fun getCommitter(): VcsUser = author
+ override fun getAuthorTime(): Long = commitObject.authorTimestamp * 1000L
+ override fun getCommitTime(): Long = commitObject.authorTimestamp * 1000L
+ override fun getFullMessage(): String = commitObject.message
+ override fun getChanges(): Collection<Change> = changes
+ override fun getChanges(parent: Int): Collection<Change> = changes
+}
blob - /dev/null
blob + 773c61207552e102eb1c5d71fe27973802e17e87 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/log/GotVcsLogProvider.kt
+package dev.nezzontli.gotvcs.log
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.vcs.VcsException
+import com.intellij.openapi.vcs.VcsKey
+import com.intellij.openapi.vcs.changes.Change
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.util.Consumer
+import com.intellij.vcs.log.Hash
+import com.intellij.vcs.log.VcsLogProperties
+import com.intellij.vcs.log.VcsLogProvider
+import com.intellij.vcs.log.VcsLogRefManager
+import com.intellij.vcs.log.VcsRef
+import com.intellij.vcs.log.VcsUser
+import com.intellij.vcs.log.impl.HashImpl
+import com.intellij.vcs.log.util.VcsUserUtil
+import com.intellij.vcsUtil.VcsUtil
+import dev.nezzontli.gotvcs.GotVcs
+import dev.nezzontli.gotvcs.cli.GotCommandLineWrapper
+import dev.nezzontli.gotvcs.cli.GotChangedPath
+import java.io.File
+import java.util.concurrent.ConcurrentHashMap
+
+/**
+ * Backs the platform's Log tab entirely by shelling out to `got` (no local
+ * index/cache beyond GotCommandLineWrapper's in-memory commit cache) -- same
+ * philosophy as the rest of this plugin: no daemon, no persisted state.
+ */
+class GotVcsLogProvider(private val commandLine: GotCommandLineWrapper = GotCommandLineWrapper()) : VcsLogProvider {
+
+ override val supportedVcs: VcsKey = GotVcs.getKey()
+
+ override val referenceManager: VcsLogRefManager = GotVcsLogRefManager()
+
+ // getCurrentBranch()/getCurrentUser() are called from EDT-bound UI code
+ // (CurrentBranchHighlighter.update(), on every visible-pack refresh -- and
+ // likely an equivalent "my commits" highlighter for getCurrentUser), so
+ // neither can shell out to `got` synchronously -- same class of bug fixed
+ // earlier for GotRepository/GotBranchWidget. readAllHashes() always runs
+ // off the EDT (it's the Log tab's own background data loading), and
+ // always runs before those highlighters can fire, so precomputing both
+ // here and serving them from cache is safe.
+ private val currentBranchCache = ConcurrentHashMap<String, String>()
+ private val currentUserCache = ConcurrentHashMap<String, VcsUser>()
+
+ override fun readAllHashes(root: VirtualFile, commitConsumer: Consumer<in com.intellij.vcs.log.TimedVcsCommit>): VcsLogProvider.LogData {
+ val workDir = File(root.path)
+ try {
+ val branch = commandLine.currentBranch(workDir)
+ if (branch != null) {
+ currentBranchCache[root.path] = branch
+ val tipId = commandLine.refs(workDir).firstOrNull { !it.isRemote && !it.isTag && it.name == branch }?.hash
+ val commitObject = tipId?.let { commandLine.catCommit(workDir, it) }
+ if (commitObject != null) {
+ currentUserCache[root.path] = VcsUserUtil.createUser(commitObject.authorName, commitObject.authorEmail)
+ }
+ } else {
+ currentBranchCache.remove(root.path)
+ }
+ } catch (e: VcsException) {
+ currentBranchCache.remove(root.path)
+ }
+ val ids = commandLine.allCommitIds(workDir)
+ val users = mutableSetOf<VcsUser>()
+ for (id in ids) {
+ val commitObject = commandLine.catCommit(workDir, id)
+ users.add(VcsUserUtil.createUser(commitObject.authorName, commitObject.authorEmail))
+ commitConsumer.consume(GotFullCommitDetails(commitObject, root))
+ }
+ val refs = buildRefs(workDir, root)
+ return SimpleLogData(refs, users)
+ }
+
+ override fun readMetadata(
+ root: VirtualFile,
+ hashes: List<String>,
+ consumer: Consumer<in com.intellij.vcs.log.VcsCommitMetadata>,
+ ) {
+ val workDir = File(root.path)
+ for (id in hashes) {
+ consumer.consume(GotFullCommitDetails(commandLine.catCommit(workDir, id), root))
+ }
+ }
+
+ override fun readFullDetails(
+ root: VirtualFile,
+ hashes: List<String>,
+ commitConsumer: Consumer<in com.intellij.vcs.log.VcsFullCommitDetails>,
+ ) {
+ val workDir = File(root.path)
+ for (id in hashes) {
+ val commitObject = commandLine.catCommit(workDir, id)
+ val parentId = commitObject.parents.firstOrNull()
+ val changes = try {
+ commandLine.changedPaths(workDir, id).map { toChange(workDir, id, parentId, it) }
+ } catch (e: VcsException) {
+ emptyList()
+ }
+ commitConsumer.consume(GotFullCommitDetails(commitObject, root, changes))
+ }
+ }
+
+ private fun toChange(workDir: File, commitId: String, parentId: String?, changed: GotChangedPath): Change {
+ val filePath = VcsUtil.getFilePath(File(workDir, changed.path), false)
+ val before = parentId?.let { GotCommitContentRevision(filePath, workDir, changed.path, commandLine, it) }
+ val after = GotCommitContentRevision(filePath, workDir, changed.path, commandLine, commitId)
+ return when (changed.code) {
+ 'A' -> Change(null, after)
+ 'D' -> Change(before, null)
+ else -> Change(before, after)
+ }
+ }
+
+ private fun buildRefs(workDir: File, root: VirtualFile): Set<VcsRef> {
+ val entries = try {
+ commandLine.refs(workDir)
+ } catch (e: VcsException) {
+ emptyList()
+ }
+ return entries.map { entry ->
+ val type = when {
+ entry.isTag -> GotRefType.TAG
+ entry.isRemote -> GotRefType.REMOTE_BRANCH
+ else -> GotRefType.LOCAL_BRANCH
+ }
+ SimpleVcsRef(HashImpl.build(entry.hash), entry.name, type, root)
+ }.toSet()
+ }
+
+ /** Best-effort: the tip commit's author, precomputed by readAllHashes(). got repos in practice have a single author, so this is accurate enough. */
+ override fun getCurrentUser(root: VirtualFile): VcsUser =
+ currentUserCache[root.path] ?: VcsUserUtil.createUser("", "")
+
+ override fun getContainingBranches(root: VirtualFile, commitHash: Hash): Collection<String> = emptyList()
+
+ override fun <T> getPropertyValue(property: VcsLogProperties.VcsLogProperty<T>): T? {
+ @Suppress("UNCHECKED_CAST")
+ return when (property) {
+ VcsLogProperties.LIGHTWEIGHT_BRANCHES -> true as T
+ else -> false as T
+ }
+ }
+
+ override fun getCurrentBranch(root: VirtualFile): String? = currentBranchCache[root.path]
+
+ override fun subscribeToRootRefreshEvents(
+ roots: Collection<VirtualFile>,
+ refresher: com.intellij.vcs.log.VcsLogRefresher,
+ ): Disposable = Disposable { }
+
+ private class SimpleLogData(
+ override val refs: Set<VcsRef>,
+ override val users: Set<VcsUser>,
+ ) : VcsLogProvider.LogData
+
+ private class SimpleVcsRef(
+ private val hash: Hash,
+ private val name: String,
+ private val type: com.intellij.vcs.log.VcsRefType,
+ private val root: VirtualFile,
+ ) : VcsRef {
+ override fun getCommitHash(): Hash = hash
+ override fun getName(): String = name
+ override fun getType(): com.intellij.vcs.log.VcsRefType = type
+ override fun getRoot(): VirtualFile = root
+ }
+}
blob - /dev/null
blob + 92899efdaeb5aaf2334dd2dc3eaf9d31ce18c13c (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/log/GotVcsLogRefManager.kt
+package dev.nezzontli.gotvcs.log
+
+import com.intellij.vcs.log.RefGroup
+import com.intellij.vcs.log.VcsLogRefManager
+import com.intellij.vcs.log.VcsRef
+import com.intellij.vcs.log.VcsRefType
+import java.io.DataInput
+import java.io.DataOutput
+
+/**
+ * got has no notion of ref grouping/favorites beyond plain
+ * branches/remote-branches/tags, so this is a flat, ungrouped manager: one
+ * [RefGroup] per ref, ordered by name.
+ */
+class GotVcsLogRefManager : VcsLogRefManager {
+
+ private val nameComparator = Comparator.comparing<VcsRef, String> { it.name }
+
+ override fun getBranchLayoutComparator(): Comparator<VcsRef> = nameComparator
+
+ override fun getLabelsOrderComparator(): Comparator<VcsRef> = nameComparator
+
+ override fun groupForBranchFilter(refs: MutableCollection<out VcsRef>): List<RefGroup> =
+ refs.sortedWith(nameComparator).map { SingleRefGroup(it) }
+
+ override fun groupForTable(refs: MutableCollection<out VcsRef>, compact: Boolean, showTagNames: Boolean): List<RefGroup> =
+ refs.sortedWith(nameComparator).map { SingleRefGroup(it) }
+
+ override fun serialize(out: DataOutput, type: VcsRefType) {
+ out.writeByte((type as? GotRefType)?.ordinal ?: GotRefType.LOCAL_BRANCH.ordinal)
+ }
+
+ override fun deserialize(input: DataInput): VcsRefType {
+ val ordinal = input.readByte().toInt()
+ return GotRefType.entries.getOrElse(ordinal) { GotRefType.LOCAL_BRANCH }
+ }
+
+ override fun isFavorite(ref: VcsRef): Boolean = false
+
+ override fun setFavorite(ref: VcsRef, favorite: Boolean) = Unit
+
+ private class SingleRefGroup(private val ref: VcsRef) : RefGroup {
+ override fun getName(): String = ref.name
+ override fun getRefs(): List<VcsRef> = listOf(ref)
+ override fun getColors(): List<java.awt.Color> = listOf(ref.type.backgroundColor)
+ }
+}
blob - 6109655e214c356044f7d12f5641baa37d9d2164 (mode 644)
blob + /dev/null
--- src/main/kotlin/dev/nezzontli/gotvcs/push/GotFullCommitDetails.kt
+++ /dev/null
-package dev.nezzontli.gotvcs.push
-
-import com.intellij.openapi.vcs.changes.Change
-import com.intellij.openapi.vfs.VirtualFile
-import com.intellij.vcs.log.Hash
-import com.intellij.vcs.log.VcsFullCommitDetails
-import com.intellij.vcs.log.VcsUser
-import com.intellij.vcs.log.impl.HashImpl
-import com.intellij.vcs.log.util.VcsUserUtil
-import dev.nezzontli.gotvcs.cli.GotCommitObject
-
-/**
- * Adapts a [GotCommitObject] (parsed from `got cat <commit-id>`) to the
- * Push dialog's outgoing-commits list. got has no per-file change list
- * cheaply available for an arbitrary historical commit here, so
- * [getChanges] is empty; the dialog only needs id/author/message/parents
- * to render its commit list.
- */
-class GotFullCommitDetails(
- private val commitObject: GotCommitObject,
- private val root: VirtualFile,
-) : VcsFullCommitDetails {
-
- private val hash: Hash = HashImpl.build(commitObject.commitId)
- private val author: VcsUser = VcsUserUtil.createUser(commitObject.authorName, commitObject.authorEmail)
-
- override fun getId(): Hash = hash
- override fun getParents(): List<Hash> = commitObject.parents.map { HashImpl.build(it) }
- override fun getTimestamp(): Long = commitObject.authorTimestamp * 1000L
- override fun getRoot(): VirtualFile = root
- override fun getSubject(): String = commitObject.message.lineSequence().firstOrNull().orEmpty()
- override fun getAuthor(): VcsUser = author
- override fun getCommitter(): VcsUser = author
- override fun getAuthorTime(): Long = commitObject.authorTimestamp * 1000L
- override fun getCommitTime(): Long = commitObject.authorTimestamp * 1000L
- override fun getFullMessage(): String = commitObject.message
- override fun getChanges(): Collection<Change> = emptyList()
- override fun getChanges(parent: Int): Collection<Change> = emptyList()
-}