commit - 86002dd96cc41e1d71f2bcd80782da4e6c1f4d0e
commit + f2c271a1a7eb4bc7a6000c5b8ff246338d3ea016
blob - 3a132b68a94c64d99f1a7cfa3f540b599cfc9c73
blob + 62c7c35916c9501165f4cadef5b9539a44a95a67
--- src/main/kotlin/dev/nezzontli/gotvcs/GotCommandLineWrapper.kt
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotCommandLineWrapper.kt
data class GotStatusEntry(val code: Char, val stagedCode: Char, val path: String)
+data class GotLogEntry(val commitId: String, val author: String, val date: String, val message: String)
+
+data class GotUpdateEntry(val code: Char, val path: String)
+
/**
* Centraliza todas las invocaciones al binario `got`. La ruta de abajo es la
* del store de Nix resuelta al momento de escribir este código
run(workDir, "cat", "-c", ":base", "-P", relativePath)
@Throws(VcsException::class)
+ fun catAt(workDir: File, commitId: String, relativePath: String): String =
+ run(workDir, "cat", "-c", commitId, "-P", relativePath)
+
+ @Throws(VcsException::class)
fun baseCommit(workDir: File): String {
val output = run(workDir, "info")
val line = output.lineSequence().firstOrNull { it.trimStart().startsWith("work tree base commit:") }
if (paths.isEmpty()) return
run(workDir, *(arrayOf("revert", "-R") + paths))
}
+
+ /** Historial vía `got log`, formato verbose por defecto (bloques separados por líneas de guiones). */
+ @Throws(VcsException::class)
+ fun log(workDir: File, relativePath: String?, limit: Int = 500): List<GotLogEntry> {
+ val args = mutableListOf("log", "-l", limit.toString())
+ if (relativePath != null) args.add(relativePath)
+ val output = run(workDir, *args.toTypedArray())
+
+ val entries = mutableListOf<GotLogEntry>()
+ var commitId: String? = null
+ var author: String? = null
+ var date: String? = null
+ val message = StringBuilder()
+
+ fun flush() {
+ val id = commitId
+ if (id != null) {
+ entries.add(GotLogEntry(id, author.orEmpty(), date.orEmpty(), message.toString().trim('\n')))
+ }
+ commitId = null
+ author = null
+ date = null
+ message.setLength(0)
+ }
+
+ for (line in output.lineSequence()) {
+ when {
+ line.isBlank() && commitId == null -> Unit
+ line.all { it == '-' } && line.isNotEmpty() -> flush()
+ line.startsWith("commit ") -> commitId = line.removePrefix("commit ").trim().substringBefore(' ')
+ line.startsWith("from: ") -> author = line.removePrefix("from: ").trim()
+ line.startsWith("date: ") -> date = line.removePrefix("date: ").trim()
+ line.startsWith(" ") -> message.appendLine(line.trimStart(' '))
+ }
+ }
+ flush()
+ return entries
+ }
+
+ @Throws(VcsException::class)
+ fun fetch(workDir: File) {
+ run(workDir, "fetch")
+ }
+
+ /** `got update`, formato de estado similar a `got status` (código + 2 espacios + path). */
+ @Throws(VcsException::class)
+ fun update(workDir: File): List<GotUpdateEntry> {
+ val output = run(workDir, "update")
+ return output.lineSequence()
+ .filter { it.isNotBlank() && it[0] != ' ' }
+ .mapNotNull { line ->
+ if (line.length < 3) return@mapNotNull null
+ GotUpdateEntry(line[0], line.substring(3))
+ }
+ .toList()
+ }
}
blob - /dev/null
blob + 16059e116fbd373039f1808615f71b732dd0aef5 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotFileRevision.kt
+package dev.nezzontli.gotvcs
+
+import com.intellij.openapi.vcs.RepositoryLocation
+import com.intellij.openapi.vcs.VcsException
+import com.intellij.openapi.vcs.history.VcsFileRevision
+import com.intellij.openapi.vcs.history.VcsRevisionNumber
+import java.io.File
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+/** Una entrada de `got log` adaptada al modelo de historial de IntelliJ. */
+class GotFileRevision(
+ private val workDir: File,
+ private val relativePath: String,
+ private val commandLine: GotCommandLineWrapper,
+ private val entry: GotLogEntry,
+) : VcsFileRevision {
+
+ // Formato real de `got log`: "Thu Jul 23 05:30:12 2026 UTC".
+ private val dateFormat = SimpleDateFormat("EEE MMM d HH:mm:ss yyyy zzz", Locale.US)
+
+ override fun getRevisionNumber(): VcsRevisionNumber = GotRevisionNumber(entry.commitId)
+
+ override fun getRevisionDate(): Date = try {
+ dateFormat.parse(entry.date)
+ } catch (e: Exception) {
+ Date(0)
+ }
+
+ override fun getAuthor(): String = entry.author
+
+ override fun getCommitMessage(): String = entry.message
+
+ override fun getBranchName(): String? = null
+
+ override fun getChangedRepositoryPath(): RepositoryLocation? = null
+
+ @Throws(VcsException::class)
+ override fun loadContent(): ByteArray = getContent()
+
+ @Throws(VcsException::class)
+ override fun getContent(): ByteArray = commandLine.catAt(workDir, entry.commitId, relativePath).toByteArray()
+}
blob - 361c64c88ee0ab4cdf957b1ed9fc7d775b12f158
blob + b433f08749a4b14d51e376f06d541a91dca5ac13
--- src/main/kotlin/dev/nezzontli/gotvcs/GotVcs.kt
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotVcs.kt
import com.intellij.openapi.vcs.changes.ChangeProvider
import com.intellij.openapi.vcs.checkin.CheckinEnvironment
import com.intellij.openapi.vcs.diff.DiffProvider
+import com.intellij.openapi.vcs.history.VcsHistoryProvider
import com.intellij.openapi.vcs.rollback.RollbackEnvironment
+import com.intellij.openapi.vcs.update.UpdateEnvironment
class GotVcs(project: Project) : AbstractVcs(project, NAME) {
private val diffProvider = GotDiffProvider(project, commandLine)
private val checkinEnvironment = GotCheckinEnvironment(project, commandLine)
private val rollbackEnvironment = GotRollbackEnvironment(project, commandLine)
+ private val historyProvider = GotVcsHistoryProvider(project, commandLine)
+ private val updateEnvironment = GotUpdateEnvironment(commandLine)
override fun getDisplayName(): String = NAME
override fun getRollbackEnvironment(): RollbackEnvironment = rollbackEnvironment
+ override fun getVcsHistoryProvider(): VcsHistoryProvider = historyProvider
+
+ override fun getUpdateEnvironment(): UpdateEnvironment = updateEnvironment
+
companion object {
const val NAME = "got"
blob - /dev/null
blob + 603a30cd5a03c463712bb3314ece4eb4115a207d (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotUpdateEnvironment.kt
+package dev.nezzontli.gotvcs
+
+import com.intellij.openapi.options.Configurable
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.util.Ref
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.VcsException
+import com.intellij.openapi.vcs.update.FileGroup
+import com.intellij.openapi.vcs.update.SequentialUpdatesContext
+import com.intellij.openapi.vcs.update.UpdateEnvironment
+import com.intellij.openapi.vcs.update.UpdateSession
+import com.intellij.openapi.vcs.update.UpdatedFiles
+import java.io.File
+
+/**
+ * "Update Project" en IntelliJ ejecuta `got fetch` (best-effort: got.conf
+ * puede no tener un remoto configurado) seguido de `got update` sobre cada
+ * raíz seleccionada.
+ */
+class GotUpdateEnvironment(private val commandLine: GotCommandLineWrapper) : UpdateEnvironment {
+
+ override fun fillGroups(updatedFiles: UpdatedFiles) = Unit
+
+ override fun updateDirectories(
+ contentRoots: Array<out FilePath>,
+ updatedFiles: UpdatedFiles,
+ progressIndicator: ProgressIndicator,
+ context: Ref<SequentialUpdatesContext>,
+ ): UpdateSession {
+ val exceptions = mutableListOf<VcsException>()
+ val workDirs = contentRoots.map { File(it.path) }.distinct()
+
+ for (workDir in workDirs) {
+ try {
+ commandLine.fetch(workDir)
+ } catch (e: VcsException) {
+ exceptions.add(e)
+ }
+
+ try {
+ for (entry in commandLine.update(workDir)) {
+ val groupId = when (entry.code) {
+ 'U', 'G' -> FileGroup.UPDATED_ID
+ 'C', '#' -> FileGroup.MERGED_WITH_CONFLICT_ID
+ 'D' -> FileGroup.REMOVED_FROM_REPOSITORY_ID
+ 'A', '!' -> FileGroup.CREATED_ID
+ else -> continue
+ }
+ updatedFiles.getGroupById(groupId)?.add(
+ File(workDir, entry.path).path,
+ GotVcs.getKey(),
+ null,
+ )
+ }
+ } catch (e: VcsException) {
+ exceptions.add(e)
+ }
+ }
+
+ return GotUpdateSession(exceptions)
+ }
+
+ override fun createConfigurable(files: MutableCollection<FilePath>): Configurable? = null
+
+ override fun validateOptions(files: MutableCollection<FilePath>): Boolean = true
+}
blob - /dev/null
blob + 6896e018526e2496a89dedb5757f2667ef065a50 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotUpdateSession.kt
+package dev.nezzontli.gotvcs
+
+import com.intellij.openapi.vcs.VcsException
+import com.intellij.openapi.vcs.update.UpdateSession
+
+class GotUpdateSession(private val exceptions: MutableList<VcsException>) : UpdateSession {
+
+ override fun getExceptions(): MutableList<VcsException> = exceptions
+
+ override fun onRefreshFilesCompleted() = Unit
+
+ override fun isCanceled(): Boolean = false
+}
blob - /dev/null
blob + 1a42e9637c573a2648e53d28c8aa02ff6bd317fc (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotVcsHistoryProvider.kt
+package dev.nezzontli.gotvcs
+
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.vcs.FilePath
+import com.intellij.openapi.vcs.ProjectLevelVcsManager
+import com.intellij.openapi.vcs.VcsException
+import com.intellij.openapi.vcs.history.DiffFromHistoryHandler
+import com.intellij.openapi.vcs.history.VcsAbstractHistorySession
+import com.intellij.openapi.vcs.history.VcsAppendableHistorySessionPartner
+import com.intellij.openapi.vcs.history.VcsDependentHistoryComponents
+import com.intellij.openapi.vcs.history.VcsFileRevision
+import com.intellij.openapi.vcs.history.VcsHistoryProvider
+import com.intellij.openapi.vcs.history.VcsHistorySession
+import com.intellij.openapi.vfs.VfsUtilCore
+import com.intellij.util.ui.ColumnInfo
+import java.io.File
+
+class GotVcsHistoryProvider(
+ private val project: Project,
+ private val commandLine: GotCommandLineWrapper,
+) : VcsHistoryProvider {
+
+ override fun getUICustomization(session: VcsHistorySession, forShortcutRegistration: javax.swing.JComponent) =
+ VcsDependentHistoryComponents.createOnlyColumns(emptyArray<ColumnInfo<*, *>>())
+
+ override fun getAdditionalActions(refresher: Runnable): Array<AnAction> = emptyArray()
+
+ override fun isDateOmittable(): Boolean = false
+
+ override fun getHelpId(): String? = null
+
+ override fun supportsHistoryForDirectories(): Boolean = true
+
+ override fun getHistoryDiffHandler(): DiffFromHistoryHandler? = null
+
+ override fun canShowHistoryFor(virtualFile: com.intellij.openapi.vfs.VirtualFile): Boolean = true
+
+ @Throws(VcsException::class)
+ override fun createSessionFor(filePath: FilePath): VcsHistorySession {
+ val vcsRoot = ProjectLevelVcsManager.getInstance(project).getVcsRootFor(filePath)
+ ?: throw VcsException("$filePath no pertenece a ningún work tree got")
+ val workDir = File(vcsRoot.path)
+ val relativePath = VfsUtilCore.getRelativePath(filePath.virtualFile ?: vcsRoot, vcsRoot)
+ ?: throw VcsException("No se pudo resolver la ruta relativa de $filePath en $workDir")
+
+ val entries = commandLine.log(workDir, relativePath.ifEmpty { null })
+ val revisions: List<VcsFileRevision> = entries.map { GotFileRevision(workDir, relativePath, commandLine, it) }
+ val baseRevision = GotRevisionNumber(commandLine.baseCommit(workDir))
+ return GotVcsHistorySession(revisions, baseRevision)
+ }
+
+ @Throws(VcsException::class)
+ override fun reportAppendableHistory(filePath: FilePath, partner: VcsAppendableHistorySessionPartner) {
+ try {
+ val session = createSessionFor(filePath) as VcsAbstractHistorySession
+ partner.reportCreatedEmptySession(session)
+ session.revisionList.forEach { partner.acceptRevision(it) }
+ } catch (e: VcsException) {
+ partner.reportException(e)
+ }
+ }
+}
blob - /dev/null
blob + bbb281713e03b2340649d2950a97f3d43e1df48b (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotVcsHistorySession.kt
+package dev.nezzontli.gotvcs
+
+import com.intellij.openapi.vcs.history.VcsAbstractHistorySession
+import com.intellij.openapi.vcs.history.VcsFileRevision
+import com.intellij.openapi.vcs.history.VcsHistorySession
+import com.intellij.openapi.vcs.history.VcsRevisionNumber
+
+class GotVcsHistorySession(
+ revisions: List<VcsFileRevision>,
+ private val baseRevision: VcsRevisionNumber,
+) : VcsAbstractHistorySession(revisions, baseRevision) {
+
+ override fun calcCurrentRevisionNumber(): VcsRevisionNumber = baseRevision
+
+ override fun copy(): VcsHistorySession = GotVcsHistorySession(revisionList, baseRevision)
+}