larc r17

155 lines ยท 4.7 KB Raw
1 package org.iwakura.larcjb.checkin
2
3 import com.intellij.openapi.diagnostic.Logger
4 import com.intellij.openapi.project.Project
5 import com.intellij.openapi.vcs.FilePath
6 import com.intellij.openapi.vcs.VcsException
7 import com.intellij.openapi.vcs.changes.Change
8 import com.intellij.openapi.vcs.changes.CommitContext
9 import com.intellij.openapi.vcs.checkin.CheckinEnvironment
10 import com.intellij.openapi.vfs.VirtualFile
11 import org.iwakura.larcjb.LarcExecutable
12 import org.iwakura.larcjb.LarcVcsSettings
13
14 /**
15 * Handles commit operations for Larc VCS.
16 */
17 class LarcCheckinEnvironment(
18 private val project: Project,
19 private val executable: LarcExecutable
20 ) : CheckinEnvironment {
21
22 private val log = Logger.getInstance(LarcCheckinEnvironment::class.java)
23
24 override fun getCheckinOperationName(): String = "Commit"
25
26 override fun getHelpId(): String? = null
27
28 override fun isRefreshAfterCommitNeeded(): Boolean = true
29
30 override fun commit(
31 changes: List<Change>,
32 commitMessage: String,
33 commitContext: CommitContext,
34 feedback: MutableSet<in String>
35 ): List<VcsException> {
36 val exceptions = mutableListOf<VcsException>()
37
38 /* Group changes by VCS root */
39 val changesByRoot = changes.groupBy { change ->
40 findVcsRoot(change)
41 }
42
43 for ((root, rootChanges) in changesByRoot) {
44 if (root == null) continue
45
46 try {
47 commitForRoot(root, rootChanges, commitMessage, feedback)
48 } catch (e: VcsException) {
49 exceptions.add(e)
50 }
51 }
52
53 return exceptions
54 }
55
56 private fun commitForRoot(
57 root: VirtualFile,
58 changes: List<Change>,
59 message: String,
60 feedback: MutableSet<in String>
61 ) {
62 /*
63 * First, stage all changed files
64 * larc add <files...>
65 */
66 val filesToAdd = changes
67 .filter { it.type != Change.Type.DELETED }
68 .mapNotNull { it.afterRevision?.file?.path }
69 .map { it.removePrefix(root.path + "/") }
70
71 if (filesToAdd.isNotEmpty()) {
72 val addResult = executable.add(root, *filesToAdd.toTypedArray())
73 if (!addResult.isSuccess) {
74 throw VcsException("Failed to stage files: ${addResult.stderr}")
75 }
76 }
77
78 /*
79 * Handle deleted files
80 * TODO(kroot): implement larc rm or handle deletions in add -A
81 */
82
83 /*
84 * Now commit
85 */
86 val author = LarcVcsSettings.getInstance(project).defaultAuthor.ifEmpty {
87 System.getProperty("user.name") ?: "unknown"
88 }
89
90 val result = executable.commit(root, message, author)
91
92 if (!result.isSuccess) {
93 throw VcsException("Commit failed: ${result.stderr}")
94 }
95
96 feedback.add("Committed to Larc: ${result.stdout}")
97 log.info("Larc commit successful: ${result.stdout}")
98 }
99
100 private fun findVcsRoot(change: Change): VirtualFile? {
101 val filePath = change.afterRevision?.file ?: change.beforeRevision?.file
102 return filePath?.let { findLarcRoot(it) }
103 }
104
105 private fun findLarcRoot(filePath: FilePath): VirtualFile? {
106 var current = filePath.virtualFile?.parent ?: filePath.virtualFileParent
107 while (current != null) {
108 if (current.findChild(".larc") != null) {
109 return current
110 }
111 current = current.parent
112 }
113 return null
114 }
115
116 override fun getDefaultMessageFor(filesToCheckin: Array<out FilePath>): String? = null
117
118 override fun scheduleMissingFileForDeletion(files: List<FilePath>): List<VcsException> {
119 /* TODO(kroot): implement file deletion scheduling */
120 return emptyList()
121 }
122
123 override fun scheduleUnversionedFilesForAddition(files: List<VirtualFile>): List<VcsException> {
124 val exceptions = mutableListOf<VcsException>()
125
126 val filesByRoot = files.groupBy { findLarcRootForFile(it) }
127
128 for ((root, rootFiles) in filesByRoot) {
129 if (root == null) continue
130
131 val relativePaths = rootFiles.map {
132 it.path.removePrefix(root.path + "/")
133 }
134
135 val result = executable.add(root, *relativePaths.toTypedArray())
136 if (!result.isSuccess) {
137 exceptions.add(VcsException("Failed to add files: ${result.stderr}"))
138 }
139 }
140
141 return exceptions
142 }
143
144 private fun findLarcRootForFile(file: VirtualFile): VirtualFile? {
145 var current: VirtualFile? = file.parent
146 while (current != null) {
147 if (current.findChild(".larc") != null) {
148 return current
149 }
150 current = current.parent
151 }
152 return null
153 }
154 }
155