package org.iwakura.larcjb.checkin import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.vcs.FilePath import com.intellij.openapi.vcs.VcsException import com.intellij.openapi.vcs.changes.Change import com.intellij.openapi.vcs.changes.CommitContext import com.intellij.openapi.vcs.checkin.CheckinEnvironment import com.intellij.openapi.vfs.VirtualFile import org.iwakura.larcjb.LarcExecutable import org.iwakura.larcjb.LarcVcsSettings /** * Handles commit operations for Larc VCS. */ class LarcCheckinEnvironment( private val project: Project, private val executable: LarcExecutable ) : CheckinEnvironment { private val log = Logger.getInstance(LarcCheckinEnvironment::class.java) override fun getCheckinOperationName(): String = "Commit" override fun getHelpId(): String? = null override fun isRefreshAfterCommitNeeded(): Boolean = true override fun commit( changes: List, commitMessage: String, commitContext: CommitContext, feedback: MutableSet ): List { val exceptions = mutableListOf() /* Group changes by VCS root */ val changesByRoot = changes.groupBy { change -> findVcsRoot(change) } for ((root, rootChanges) in changesByRoot) { if (root == null) continue try { commitForRoot(root, rootChanges, commitMessage, feedback) } catch (e: VcsException) { exceptions.add(e) } } return exceptions } private fun commitForRoot( root: VirtualFile, changes: List, message: String, feedback: MutableSet ) { /* * First, stage all changed files * larc add */ val filesToAdd = changes .filter { it.type != Change.Type.DELETED } .mapNotNull { it.afterRevision?.file?.path } .map { it.removePrefix(root.path + "/") } if (filesToAdd.isNotEmpty()) { val addResult = executable.add(root, *filesToAdd.toTypedArray()) if (!addResult.isSuccess) { throw VcsException("Failed to stage files: ${addResult.stderr}") } } /* * Handle deleted files * TODO(kroot): implement larc rm or handle deletions in add -A */ /* * Now commit */ val author = LarcVcsSettings.getInstance(project).defaultAuthor.ifEmpty { System.getProperty("user.name") ?: "unknown" } val result = executable.commit(root, message, author) if (!result.isSuccess) { throw VcsException("Commit failed: ${result.stderr}") } feedback.add("Committed to Larc: ${result.stdout}") log.info("Larc commit successful: ${result.stdout}") } private fun findVcsRoot(change: Change): VirtualFile? { val filePath = change.afterRevision?.file ?: change.beforeRevision?.file return filePath?.let { findLarcRoot(it) } } private fun findLarcRoot(filePath: FilePath): VirtualFile? { var current = filePath.virtualFile?.parent ?: filePath.virtualFileParent while (current != null) { if (current.findChild(".larc") != null) { return current } current = current.parent } return null } override fun getDefaultMessageFor(filesToCheckin: Array): String? = null override fun scheduleMissingFileForDeletion(files: List): List { /* TODO(kroot): implement file deletion scheduling */ return emptyList() } override fun scheduleUnversionedFilesForAddition(files: List): List { val exceptions = mutableListOf() val filesByRoot = files.groupBy { findLarcRootForFile(it) } for ((root, rootFiles) in filesByRoot) { if (root == null) continue val relativePaths = rootFiles.map { it.path.removePrefix(root.path + "/") } val result = executable.add(root, *relativePaths.toTypedArray()) if (!result.isSuccess) { exceptions.add(VcsException("Failed to add files: ${result.stderr}")) } } return exceptions } private fun findLarcRootForFile(file: VirtualFile): VirtualFile? { var current: VirtualFile? = file.parent while (current != null) { if (current.findChild(".larc") != null) { return current } current = current.parent } return null } }