Commit Diff


commit - 8f270315ac1a03425e7daccc1db3b14cd7080df8
commit + 701fffebad34d0f36f5fd2165569f42d177ffef9
blob - /dev/null
blob + 5d5d3b53a52c1f7cb2b933f3284eaa56abb6524a (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotChangeProvider.kt
@@ -0,0 +1,67 @@
+package dev.nezzontli.gotvcs
+
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.vcs.FileStatus
+import com.intellij.openapi.vcs.changes.Change
+import com.intellij.openapi.vcs.changes.ChangeListManagerGate
+import com.intellij.openapi.vcs.changes.ChangeProvider
+import com.intellij.openapi.vcs.changes.ChangelistBuilder
+import com.intellij.openapi.vcs.changes.CurrentContentRevision
+import com.intellij.openapi.vcs.changes.LocallyDeletedChange
+import com.intellij.openapi.vcs.changes.VcsDirtyScope
+import com.intellij.openapi.vfs.LocalFileSystem
+import com.intellij.openapi.vfs.VirtualFile
+import com.intellij.vcsUtil.VcsUtil
+import java.io.File
+
+class GotChangeProvider(private val commandLine: GotCommandLineWrapper) : ChangeProvider {
+
+    override fun getChanges(
+        dirtyScope: VcsDirtyScope,
+        builder: ChangelistBuilder,
+        progress: ProgressIndicator,
+        addGate: ChangeListManagerGate,
+    ) {
+        for (root in dirtyScope.affectedContentRoots) {
+            val workDir = File(root.path)
+            val entries = commandLine.status(workDir)
+
+            for (entry in entries) {
+                val absoluteFile = File(workDir, entry.path)
+                val filePath = VcsUtil.getFilePath(absoluteFile, false)
+
+                when (entry.code) {
+                    '?' -> {
+                        val virtualFile = LocalFileSystem.getInstance().findFileByIoFile(absoluteFile)
+                        if (virtualFile != null) builder.processUnversionedFile(virtualFile)
+                    }
+
+                    '!' -> builder.processLocallyDeletedFile(LocallyDeletedChange(filePath, filePath.path))
+
+                    'M', 'm', 'C' -> {
+                        val status = if (entry.code == 'C') FileStatus.MERGED_WITH_CONFLICTS else FileStatus.MODIFIED
+                        val before = GotContentRevision(filePath, workDir, entry.path, commandLine)
+                        val after = CurrentContentRevision(filePath)
+                        builder.processChange(Change(before, after, status), GotVcs.getKey())
+                    }
+
+                    'A' -> {
+                        val after = CurrentContentRevision(filePath)
+                        builder.processChange(Change(null, after, FileStatus.ADDED), GotVcs.getKey())
+                    }
+
+                    'D' -> {
+                        val before = GotContentRevision(filePath, workDir, entry.path, commandLine)
+                        builder.processChange(Change(before, null, FileStatus.DELETED), GotVcs.getKey())
+                    }
+
+                    else -> Unit
+                }
+            }
+        }
+    }
+
+    override fun isModifiedDocumentTrackingRequired(): Boolean = false
+
+    override fun doCleanup(files: MutableList<out VirtualFile>) = Unit
+}
blob - /dev/null
blob + ac1cc93524bbae9807b2bb24fc2210a6b8af6352 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotCommandLineWrapper.kt
@@ -0,0 +1,58 @@
+package dev.nezzontli.gotvcs
+
+import com.intellij.execution.ExecutionException
+import com.intellij.execution.configurations.GeneralCommandLine
+import com.intellij.execution.util.ExecUtil
+import com.intellij.openapi.vcs.VcsException
+import java.io.File
+import java.nio.charset.StandardCharsets
+
+data class GotStatusEntry(val code: Char, val stagedCode: 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
+ * (readlink -f /etc/profiles/per-user/ale/bin/idea muestra el store path del
+ * IDE; el de got se resolvió igual desde /run/current-system/sw/bin/got).
+ * Cambia con cada rebuild del sistema -> deuda para GotConfigurable (Fase 6).
+ */
+class GotCommandLineWrapper {
+
+    private fun binaryPath(): String {
+        val nixPath = File("/run/current-system/sw/bin/got")
+        return if (nixPath.canExecute()) nixPath.path else "got"
+    }
+
+    private fun run(workDir: File, vararg args: String): String {
+        val commandLine = GeneralCommandLine(binaryPath(), *args)
+            .withWorkDirectory(workDir)
+            .withCharset(StandardCharsets.UTF_8)
+        val output = try {
+            ExecUtil.execAndGetOutput(commandLine)
+        } catch (e: ExecutionException) {
+            throw VcsException("No se pudo ejecutar got ${args.joinToString(" ")}: ${e.message}", e)
+        }
+        if (output.exitCode != 0) {
+            throw VcsException("got ${args.joinToString(" ")} salió con código ${output.exitCode}: ${output.stderr.trim()}")
+        }
+        return output.stdout
+    }
+
+    @Throws(VcsException::class)
+    fun status(workDir: File): List<GotStatusEntry> {
+        val output = run(workDir, "status")
+        return output.lineSequence()
+            .filter { it.isNotBlank() }
+            .map { line ->
+                val code = line[0]
+                val stagedCode = line.getOrElse(1) { ' ' }
+                val path = line.substring(minOf(3, line.length))
+                GotStatusEntry(code, stagedCode, path)
+            }
+            .toList()
+    }
+
+    @Throws(VcsException::class)
+    fun catAtBase(workDir: File, relativePath: String): String =
+        run(workDir, "cat", "-c", ":base", "-P", relativePath)
+}
blob - /dev/null
blob + 71fc5d1ce6d080d0810c34ed9caf00cac683e449 (mode 644)
--- /dev/null
+++ src/main/kotlin/dev/nezzontli/gotvcs/GotContentRevision.kt
@@ -0,0 +1,23 @@
+package dev.nezzontli.gotvcs
+
+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 java.io.File
+
+/** Contenido de un archivo en el commit base del work tree, vía `got cat -c :base`. */
+class GotContentRevision(
+    private val filePath: FilePath,
+    private val workDir: File,
+    private val relativePath: String,
+    private val commandLine: GotCommandLineWrapper,
+) : ContentRevision {
+
+    @Throws(VcsException::class)
+    override fun getContent(): String = commandLine.catAtBase(workDir, relativePath)
+
+    override fun getFile(): FilePath = filePath
+
+    override fun getRevisionNumber(): VcsRevisionNumber = VcsRevisionNumber.NULL
+}