Q7FXTHVUPVAFMNY277C3NFJO3VXLZU5G6C6UYSD5QPURHSG3A7OQC
MTPTFTHGAOKQGRUDXC55AM6XJHZZZ5EF6FPVXKFVCUYVXJNEANYQC
FRFFQV7VNYKGCA7ZAOSRPC2HHYTAIZ6AGGR7A5QEV6QPAQGFDYGAC
GGYFPXND4VBCROZZXTKAP7Y4JOP2OOYQAFVLMUE7SLFM225EUSIAC
QXUEMZ3B2FUHFUC7ZZHJMH5FVWLEMEYXUMFA6JNXTJKIVZNMRIOAC
FNNW5IEAXQ43WKB6QSQB7DFLG3Y3T5FYPXIUX7KQ2URR2GU3QLTAC
6CR2EFUN7JXFHCBTNX3WWOOP4WFOCFO6KSPEBN6V6J5HFZO2LHNQC
OPFG6CZ26PPTGTH7ULLRQGZGR3YEIEJOV5W2E3WN7PFRZS62CVLQC
@OptIn(ExperimentalPathApi::class)
fun loadStateInRevision(revisionHash: String,
project: Project,
root: Path,
filePath: Path): String {
val tmpTarget = Paths.get(project.baseDir.path, ".idea", "dracon_diffs", revisionHash)
if (Files.exists(tmpTarget)) {
Files.walk(tmpTarget).use { walk ->
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete)
}
}
Files.createDirectories(tmpTarget)
copyFolder(root, tmpTarget, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING)
val revisions = pijul(project).latestRevisionNumber(project, tmpTarget).result
if (revisions != null && revisions.hash == revisionHash) {
val rollbackOp = pijul(project).reset(project, tmpTarget)
} else {
val resetOp = pijul(project).reset(project, tmpTarget)
val rollbackOp = pijul(project).rollbackTo(revisionHash, project, tmpTarget)
}
@OptIn(ExperimentalPathApi::class)
constructor(project: Project,
vcsRoot: Path,
filePath: FilePath,
revision: PijulRevisionNumber,
author_: String?,
message: String?,
branch: String?): this(project, vcsRoot, Paths.get(filePath.path).relativeTo(vcsRoot), revision, author_, message, branch)
package com.github.jonathanxd.dracon.provider
import com.github.jonathanxd.dracon.content.PijulContentRevision
import com.github.jonathanxd.dracon.pijul.pijul
import com.github.jonathanxd.dracon.revision.PijulRevisionNumber
import com.intellij.openapi.components.Service
import com.intellij.openapi.project.Project
import com.intellij.openapi.vcs.FilePath
import com.intellij.openapi.vcs.FileStatus
import com.intellij.openapi.vcs.ProjectLevelVcsManager
import com.intellij.openapi.vcs.changes.ContentRevision
import com.intellij.openapi.vcs.diff.DiffProvider
import com.intellij.openapi.vcs.diff.ItemLatestState
import com.intellij.openapi.vcs.history.VcsRevisionNumber
import com.intellij.openapi.vfs.VirtualFile
import java.nio.file.Paths
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.relativeTo
@Service
class PijulDiffProvider(val project: Project) : DiffProvider {
override fun getCurrentRevision(file: VirtualFile): VcsRevisionNumber? {
val root = ProjectLevelVcsManager.getInstance(this.project).getVcsRootFor(file)?.toNioPath() ?: return null
return pijul(project).latestRevisionNumberForPath(this.project, root, file.toNioPath()).result
}
@OptIn(ExperimentalPathApi::class)
fun getCurrentRevision(path: FilePath): VcsRevisionNumber? {
val root = ProjectLevelVcsManager.getInstance(this.project).getVcsRootFor(path)?.toNioPath() ?: return null
val filePath = path.ioFile.toPath().relativeTo(root)
return pijul(project).latestRevisionNumberForPath(this.project, root, filePath).result
}
override fun getLastRevision(virtualFile: VirtualFile): ItemLatestState? {
val current = this.getCurrentRevision(virtualFile) ?: return null
return ItemLatestState(current, virtualFile.exists(), true)
}
override fun getLastRevision(filePath: FilePath): ItemLatestState? {
val current = this.getCurrentRevision(filePath) ?: return null
return ItemLatestState(current, filePath.ioFile.exists(), true)
}
override fun createFileContent(revisionNumber: VcsRevisionNumber, selectedFile: VirtualFile): ContentRevision? {
val root = ProjectLevelVcsManager.getInstance(this.project).getVcsRootFor(selectedFile)?.toNioPath() ?: return null
return PijulContentRevision(
root,
selectedFile.toNioPath(),
revisionNumber as? PijulRevisionNumber ?: return null,
this.project
)
}
override fun getLatestCommittedRevision(vcsRoot: VirtualFile?): VcsRevisionNumber? {
return null
}
}
package com.github.jonathanxd.dracon.config
import com.intellij.dvcs.branch.DvcsCompareSettings
import com.intellij.dvcs.branch.DvcsSyncSettings
import com.intellij.openapi.components.*
import com.intellij.util.xmlb.annotations.OptionTag
import com.intellij.vcs.log.VcsUser
@State(name = "Pijul.Settings", storages = [Storage(StoragePathMacros.WORKSPACE_FILE)])
class PijulSettings : SimplePersistentStateComponent<PijulOptions>(PijulOptions()), DvcsSyncSettings, DvcsCompareSettings {
override fun getSyncSetting(): DvcsSyncSettings.Value =
this.state.rootSync
fun getAuthor(): String? {
return this.state.author
}
fun saveAuthor(author: VcsUser) { // Pijul still does not support specifying emails?
this.state.author = author.name
}
override fun setSyncSetting(syncSetting: DvcsSyncSettings.Value) {
this.state.rootSync = syncSetting
}
override fun shouldSwapSidesInCompareBranches(): Boolean =
this.state.isSwapSidesInCompareBranches
override fun setSwapSidesInCompareBranches(value: Boolean) {
this.state.isSwapSidesInCompareBranches = value
}
}
class PijulOptions : BaseState() {
@get:OptionTag("PATH_TO_PIJUL")
var pathToGit by string()
@get:OptionTag("AUTHOR")
var author by string()
@get:OptionTag("ROOT_SYNC")
var rootSync by enum(DvcsSyncSettings.Value.NOT_DECIDED)
@get:OptionTag("SWAP_SIDES_IN_COMPARE_BRANCHES")
var isSwapSidesInCompareBranches by property(false)
}
}
}
override fun record(
project: Project,
root: Path,
files: List<Path>,
author: VcsUser?,
message: String
): PijulOperationResult<Boolean> {
val arguments = mutableListOf<String>("record")
if (author != null) {
arguments.add("--author")
arguments.add(author.name)
}
arguments.add("--message")
arguments.add(message)
if (files.isNotEmpty()) {
arguments.add("--")
arguments.addAll(files.map { it.toString() })
} else {
return PijulOperationResult("record", NonZeroExitStatusCode(-1, "Empty list of paths to record"), false)
override fun latestRevisionNumberForPath(
project: Project,
root: Path,
filePath: Path
): PijulOperationResult<PijulRevisionNumber> {
val log = this.log(project, root)
return PijulOperationResult(log.operation, log.statusCode,
log.result?.entries?.firstOrNull {
it.hunks.filterIsInstance<HunkWithPath>().firstOrNull {
filePath.toString().equals(it.resolvedPath, ignoreCase = true)
} != null
}?.let {
PijulRevisionNumber(it.changeHash, it.date)
}
)
}
package com.github.jonathanxd.dracon.checkin
import com.github.jonathanxd.dracon.config.PijulSettings
import com.github.jonathanxd.dracon.i18n.DraconBundle
import com.github.jonathanxd.dracon.pijul.SuccessStatusCode
import com.github.jonathanxd.dracon.pijul.pijul
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.Balloon
import com.intellij.openapi.util.Key
import com.intellij.openapi.vcs.CheckinProjectPanel
import com.intellij.openapi.vcs.FilePath
import com.intellij.openapi.vcs.ProjectLevelVcsManager
import com.intellij.openapi.vcs.VcsException
import com.intellij.openapi.vcs.changes.*
import com.intellij.openapi.vcs.checkin.CheckinChangeListSpecificComponent
import com.intellij.openapi.vcs.checkin.CheckinEnvironment
import com.intellij.openapi.vcs.ui.RefreshableOnComponent
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.GridBag
import com.intellij.util.ui.JBUI
import com.intellij.vcs.commit.*
import com.intellij.vcs.log.VcsUser
import com.intellij.vcs.log.VcsUserEditor
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import java.awt.event.*
import java.util.*
import javax.swing.JComponent
import javax.swing.JPanel
import kotlin.io.path.ExperimentalPathApi
import kotlin.io.path.relativeTo
val COMMIT_AUTHOR_KEY = Key.create<VcsUser>("Pijul.Commit.Author")
val COMMIT_AUTHOR_DATE_KEY = Key.create<Date>("Pijul.Commit.AuthorDate")
internal var CommitContext.commitAuthor: VcsUser? by commitProperty(COMMIT_AUTHOR_KEY, null)
internal var CommitContext.commitAuthorDate: Date? by commitProperty(COMMIT_AUTHOR_DATE_KEY, null)
@Service
class PijulCheckingEnvironment(val project: Project) : CheckinEnvironment {
override fun createCommitOptions(
commitPanel: CheckinProjectPanel,
commitContext: CommitContext
): RefreshableOnComponent? {
return PijulCheckinOptions(commitPanel, commitContext)
}
override fun getHelpId(): String? = null
override fun getCheckinOperationName(): String =
DraconBundle.Record.name
override fun scheduleMissingFileForDeletion(files: MutableList<out FilePath>): MutableList<VcsException> {
return mutableListOf()
}
override fun scheduleUnversionedFilesForAddition(files: MutableList<out VirtualFile>): MutableList<VcsException> {
return mutableListOf()
}
override fun isRefreshAfterCommitNeeded(): Boolean = true
@OptIn(ExperimentalPathApi::class)
override fun commit(
changes: MutableList<out Change>,
commitMessage: String,
commitContext: CommitContext,
feedback: MutableSet<in String>
): MutableList<VcsException>? {
val vcsManager = ProjectLevelVcsManager.getInstance(this.project)
val root = vcsManager.allVcsRoots.first().path.toNioPath()
val paths = changes.map {
val root = vcsManager.getVcsRootFor(it.afterRevision!!.file)!!.toNioPath()
val relative = it.afterRevision!!.file.ioFile.toPath().relativeTo(root)
relative
}
println(paths)
val author = commitContext.commitAuthor
val record = pijul(project).record(project, root, paths, author, commitMessage)
return if (record.statusCode !is SuccessStatusCode) {
mutableListOf(
VcsException("Operation ${record.operation}, failed with statusCode: ${record.statusCode}")
)
} else {
super.commit(changes, commitMessage, commitContext, feedback)
}
}
class PijulCheckinOptions(val commitPanel: CheckinProjectPanel, val commitContext: CommitContext) : RefreshableOnComponent, Disposable {
val optionsUi = PijulOptionsUi(commitPanel, commitContext)
override fun refresh() {
optionsUi.refresh()
}
override fun saveState() {
optionsUi.saveState()
}
override fun restoreState() {
optionsUi.restoreState()
}
override fun getComponent(): JComponent {
return optionsUi.component
}
override fun dispose() {
optionsUi.dispose()
}
}
class PijulOptionsUi(private val commitPanel: CheckinProjectPanel,
private val commitContext: CommitContext) : RefreshableOnComponent,
CheckinChangeListSpecificComponent,
CommitAuthorListener,
Disposable {
private val CheckinProjectPanel.commitAuthorTracker: CommitAuthorTracker? get() = commitWorkflowHandler as? CommitAuthorTracker
private val project get() = commitPanel.project
private val settings = project.service<PijulSettings>()
private var authorDate: Date? = null
private val panel = JPanel(GridBagLayout())
private val authorField = VcsUserEditor(project, getKnownCommitAuthors())
init {
authorField.addFocusListener(object : FocusAdapter() {
override fun focusLost(e: FocusEvent) {
updateCurrentCommitAuthor()
}
})
buildLayout()
}
private fun buildLayout() = panel.apply {
val gb = GridBag().setDefaultAnchor(GridBagConstraints.WEST).setDefaultInsets(JBUI.insets(2))
val authorLabel = JBLabel(DraconBundle.message("record.author")).apply { labelFor = authorField }
add(authorLabel, gb.nextLine().next())
add(authorField, gb.next().fillCellHorizontally().weightx(1.0))
}
override fun dispose() = Unit
override fun getComponent(): JComponent = panel
override fun restoreState() {
if (commitPanel.isNonModalCommit) {
commitPanel.commitAuthorTracker?.addCommitAuthorListener(this, this)
}
refresh()
}
override fun refresh() {
refresh(null)
commitAuthorChanged()
}
override fun saveState() {
val author = getAuthor()
commitContext.apply {
commitAuthor = author
commitAuthorDate = authorDate
}
settings.apply {
author?.let { saveAuthor(it) }
}
}
override fun onChangeListSelected(list: LocalChangeList) {
refresh(list)
panel.revalidate()
panel.repaint()
}
private fun refresh(changeList: LocalChangeList?) {
setAuthor(changeList?.author)
authorDate = changeList?.authorDate
}
fun getAuthor(): VcsUser? = authorField.user
private fun setAuthor(author: VcsUser?) {
val isAuthorNull = author == null
authorField.user = author.takeUnless { isAuthorNull }
}
private fun updateCurrentCommitAuthor() {
commitPanel.commitAuthorTracker?.commitAuthor = getAuthor()
}
override fun commitAuthorChanged() {
val newAuthor = commitPanel.commitAuthorTracker?.commitAuthor
if (getAuthor() != newAuthor) setAuthor(newAuthor)
}
private fun getKnownCommitAuthors(): List<String> =
(VcsUserEditor.getAllUsers(project) + settings.getAuthor()?.let { listOf(it) }.orEmpty()).distinct().sorted()
}
}