mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-143759 Repository contains unmerged paths. Merge not work for me
This commit is contained in:
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.settingsRepository
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
@@ -6,18 +21,15 @@ import com.intellij.openapi.fileTypes.StdFileTypes
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vcs.merge.MergeDialogCustomizer
|
||||
import com.intellij.openapi.vcs.merge.MergeProvider2
|
||||
import com.intellij.openapi.vcs.merge.MergeSession
|
||||
import com.intellij.openapi.vcs.merge.MultipleFileMergeDialog
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.testFramework.LightVirtualFile
|
||||
import com.intellij.util.PathUtilRt
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.Arrays
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
@@ -158,38 +170,25 @@ fun File.removeWithParentsIfEmpty(root: File, isFile: Boolean = true) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// kotlin bug, cannot be val (.NoSuchMethodError: org.jetbrains.settingsRepository.SettingsRepositoryPackage.getMARKER_ACCEPT_MY()[B)
|
||||
TestOnly object AM {
|
||||
val MARKER_ACCEPT_MY: ByteArray = "__accept my__".toByteArray()
|
||||
val MARKER_ACCEPT_THEIRS: ByteArray = "__accept theirs__".toByteArray()
|
||||
}
|
||||
|
||||
var conflictResolver: ((files: List<VirtualFile>, mergeProvider: MergeProvider2) -> Unit)? = null
|
||||
|
||||
fun resolveConflicts(files: List<VirtualFile>, mergeProvider: MergeProvider2): List<VirtualFile> {
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode()) {
|
||||
val mergeSession = mergeProvider.createMergeSession(files)
|
||||
for (file in files) {
|
||||
val mergeData = mergeProvider.loadRevisions(file)
|
||||
if (Arrays.equals(mergeData.CURRENT, AM.MARKER_ACCEPT_MY) || Arrays.equals(mergeData.LAST, AM.MARKER_ACCEPT_THEIRS)) {
|
||||
mergeSession.conflictResolvedForFile(file, MergeSession.Resolution.AcceptedYours)
|
||||
}
|
||||
else if (Arrays.equals(mergeData.CURRENT, AM.MARKER_ACCEPT_THEIRS) || Arrays.equals(mergeData.LAST, AM.MARKER_ACCEPT_MY)) {
|
||||
mergeSession.conflictResolvedForFile(file, MergeSession.Resolution.AcceptedTheirs)
|
||||
}
|
||||
else if (Arrays.equals(mergeData.LAST, AM.MARKER_ACCEPT_MY)) {
|
||||
file.setBinaryContent(mergeData.LAST!!)
|
||||
mergeProvider.conflictResolvedForFile(file)
|
||||
}
|
||||
else {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
if (conflictResolver == null) {
|
||||
throw CannotResolveConflictInTestMode()
|
||||
}
|
||||
else {
|
||||
conflictResolver!!(files, mergeProvider)
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
var processedFiles: List<VirtualFile>? = null
|
||||
invokeAndWaitIfNeed {
|
||||
val fileMergeDialog = MultipleFileMergeDialog(null, files, mergeProvider, MergeDialogCustomizer())
|
||||
val fileMergeDialog = MultipleFileMergeDialog(null, files, mergeProvider, object : MergeDialogCustomizer() {
|
||||
override fun getMultipleFileDialogTitle() = "Settings Repository: Files Merged with Conflicts"
|
||||
})
|
||||
fileMergeDialog.show()
|
||||
processedFiles = fileMergeDialog.getProcessedFiles()
|
||||
}
|
||||
|
||||
@@ -289,6 +289,4 @@ class IcsApplicationLoadListener : ApplicationLoadListener {
|
||||
|
||||
icsManager.beforeApplicationLoaded(application)
|
||||
}
|
||||
}
|
||||
|
||||
class NoRemoteRepositoryException(cause: Throwable) : RuntimeException(cause.getMessage(), cause)
|
||||
}
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.settingsRepository.git
|
||||
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
@@ -6,9 +21,8 @@ import com.intellij.openapi.vcs.merge.MergeProvider2
|
||||
import com.intellij.openapi.vcs.merge.MergeSession
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.ArrayUtil
|
||||
import com.intellij.util.ui.ColumnInfo
|
||||
import org.eclipse.jgit.diff.RawText
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import org.eclipse.jgit.lib.Repository
|
||||
import org.jetbrains.jgit.dirCache.deletePath
|
||||
import org.jetbrains.jgit.dirCache.writePath
|
||||
@@ -16,7 +30,7 @@ import org.jetbrains.settingsRepository.RepositoryVirtualFile
|
||||
import java.nio.CharBuffer
|
||||
import java.util.ArrayList
|
||||
|
||||
private fun conflictsToVirtualFiles(map: Map<String, org.eclipse.jgit.merge.MergeResult<*>>): MutableList<VirtualFile> {
|
||||
private fun conflictsToVirtualFiles(map: Map<String, Any>): MutableList<VirtualFile> {
|
||||
val result = ArrayList<VirtualFile>(map.size())
|
||||
for (path in map.keySet()) {
|
||||
result.add(RepositoryVirtualFile(path))
|
||||
@@ -24,7 +38,12 @@ private fun conflictsToVirtualFiles(map: Map<String, org.eclipse.jgit.merge.Merg
|
||||
return result
|
||||
}
|
||||
|
||||
class JGitMergeProvider(private val repository: Repository, private val myCommit: ObjectId, private val theirsCommit: ObjectId, private val conflicts: Map<String, org.eclipse.jgit.merge.MergeResult<*>>) : MergeProvider2 {
|
||||
/**
|
||||
* If content null:
|
||||
* Ours or Theirs - deleted.
|
||||
* Base - missed (no base).
|
||||
*/
|
||||
class JGitMergeProvider<T>(private val repository: Repository, private val conflicts: Map<String, T>, private val pathToContent: Map<String, T>.(path: String, index: Int) -> ByteArray?) : MergeProvider2 {
|
||||
override fun createMergeSession(files: List<VirtualFile>) = JGitMergeSession()
|
||||
|
||||
override fun conflictResolvedForFile(file: VirtualFile) {
|
||||
@@ -42,22 +61,23 @@ class JGitMergeProvider(private val repository: Repository, private val myCommit
|
||||
}
|
||||
}
|
||||
|
||||
// cannot be private due to Kotlin bug
|
||||
fun addFile(bytes: ByteArray, file: VirtualFile, size: Int = bytes.size()) {
|
||||
private fun addFile(bytes: ByteArray, file: VirtualFile, size: Int = bytes.size()) {
|
||||
repository.writePath(file.getPath(), bytes, size)
|
||||
}
|
||||
|
||||
override fun isBinary(file: VirtualFile) = file.getFileType().isBinary()
|
||||
|
||||
override fun loadRevisions(file: VirtualFile): MergeData {
|
||||
val sequences = conflicts[file.getPath()]!!.getSequences()
|
||||
val path = file.getPath()
|
||||
val mergeData = MergeData()
|
||||
mergeData.ORIGINAL = (sequences[0] as RawText).getContent()
|
||||
mergeData.CURRENT = (sequences[1] as RawText).getContent()
|
||||
mergeData.LAST = (sequences[2] as RawText).getContent()
|
||||
mergeData.ORIGINAL = getContentOrEmpty(path, 0)
|
||||
mergeData.CURRENT = getContentOrEmpty(path, 1)
|
||||
mergeData.LAST = getContentOrEmpty(path, 2)
|
||||
return mergeData
|
||||
}
|
||||
|
||||
private fun getContentOrEmpty(path: String, index: Int) = conflicts.pathToContent(path, index) ?: ArrayUtil.EMPTY_BYTE_ARRAY
|
||||
|
||||
private inner class JGitMergeSession : MergeSession {
|
||||
override fun getMergeInfoColumns(): Array<ColumnInfo<out Any?, out Any?>> {
|
||||
return arrayOf(StatusColumn(false), StatusColumn(true))
|
||||
@@ -71,19 +91,19 @@ class JGitMergeProvider(private val repository: Repository, private val myCommit
|
||||
}
|
||||
else {
|
||||
val content = getContent(file, resolution == MergeSession.Resolution.AcceptedTheirs)
|
||||
if (content == RawText.EMPTY_TEXT) {
|
||||
if (content == null) {
|
||||
repository.deletePath(file.getPath())
|
||||
}
|
||||
else {
|
||||
addFile(content.getContent(), file)
|
||||
addFile(content, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getContent(file: VirtualFile, isTheirs: Boolean) = conflicts[file.getPath()]!!.getSequences()[if (isTheirs) 2 else 1] as RawText
|
||||
private fun getContent(file: VirtualFile, isTheirs: Boolean) = conflicts.pathToContent(file.getPath(), if (isTheirs) 2 else 1)
|
||||
|
||||
inner class StatusColumn(private val isTheirs: Boolean) : ColumnInfo<VirtualFile, String>(if (isTheirs) "Theirs" else "Yours") {
|
||||
override fun valueOf(file: VirtualFile?) = if (getContent(file!!, isTheirs) == RawText.EMPTY_TEXT) "Deleted" else "Modified"
|
||||
override fun valueOf(file: VirtualFile?) = if (getContent(file!!, isTheirs) == null) "Deleted" else "Modified"
|
||||
|
||||
override fun getMaxStringValue() = "Modified"
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.jetbrains.settingsRepository.git
|
||||
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.containers.hash.LinkedHashMap
|
||||
import org.eclipse.jgit.api.MergeCommand.FastForwardMode
|
||||
import org.eclipse.jgit.api.MergeResult
|
||||
import org.eclipse.jgit.api.MergeResult.MergeStatus
|
||||
@@ -24,6 +26,8 @@ import org.eclipse.jgit.api.errors.CheckoutConflictException
|
||||
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException
|
||||
import org.eclipse.jgit.api.errors.JGitInternalException
|
||||
import org.eclipse.jgit.api.errors.NoHeadException
|
||||
import org.eclipse.jgit.diff.RawText
|
||||
import org.eclipse.jgit.diff.Sequence
|
||||
import org.eclipse.jgit.dircache.DirCacheCheckout
|
||||
import org.eclipse.jgit.internal.JGitText
|
||||
import org.eclipse.jgit.lib.*
|
||||
@@ -38,7 +42,6 @@ import org.eclipse.jgit.treewalk.FileTreeIterator
|
||||
import org.jetbrains.settingsRepository.*
|
||||
import java.io.IOException
|
||||
import java.text.MessageFormat
|
||||
import java.util.ArrayList
|
||||
|
||||
open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndicator?, val commitMessageFormatter: CommitMessageFormatter = IdeaCommitMessageFormatter()) {
|
||||
val repository = manager.repository
|
||||
@@ -53,13 +56,30 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
LOG.debug("Pull")
|
||||
|
||||
val repository = manager.repository
|
||||
val repositoryState = repository.getRepositoryState()
|
||||
var repositoryState = repository.getRepositoryState()
|
||||
if (repositoryState == RepositoryState.MERGING) {
|
||||
val conflicts = LinkedHashMap<String, Array<ByteArray?>>()
|
||||
val dirCache = repository.readDirCache()
|
||||
val reader = repository.newObjectReader()
|
||||
reader.use {
|
||||
for (i in 0..(dirCache.getEntryCount() - 1)) {
|
||||
val entry = dirCache.getEntry(i)
|
||||
if (!entry.isMerged()) {
|
||||
conflicts.getOrPut(entry.getPathString(), { arrayOfNulls<ByteArray>(3) })[entry.getStage() - 1] = reader.open(entry.getObjectId(), Constants.OBJ_BLOB).getCachedBytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolveConflicts(JGitMergeProvider(repository, conflicts, { path, index -> get(path)!!.get(index) }), conflictsToVirtualFiles(conflicts), repository)
|
||||
// compute new state
|
||||
repositoryState = repository.getRepositoryState()
|
||||
}
|
||||
|
||||
if (repositoryState != RepositoryState.SAFE) {
|
||||
LOG.warn(MessageFormat.format(JGitText.get().cannotPullOnARepoWithState, repositoryState.name()))
|
||||
}
|
||||
|
||||
val refToMerge = prefetchedRefToMerge ?: fetch() ?: return null
|
||||
|
||||
var refToMerge = prefetchedRefToMerge ?: fetch() ?: return null
|
||||
val mergeResult = merge(refToMerge, mergeStrategy, commitMessage = commitMessage)
|
||||
val mergeStatus = mergeResult.mergeStatus
|
||||
if (LOG.isDebugEnabled()) {
|
||||
@@ -67,31 +87,7 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
}
|
||||
|
||||
if (mergeStatus == MergeStatus.CONFLICTING) {
|
||||
val mergedCommits = mergeResult.mergedCommits
|
||||
assert(mergedCommits.size() == 2)
|
||||
val conflicts = mergeResult.conflicts!!
|
||||
var unresolvedFiles = conflictsToVirtualFiles(conflicts)
|
||||
val mergeProvider = JGitMergeProvider(repository, mergedCommits[0]!!, mergedCommits[1]!!, conflicts)
|
||||
val resolvedFiles: List<VirtualFile>
|
||||
val mergedFiles = ArrayList<String>()
|
||||
while (true) {
|
||||
resolvedFiles = resolveConflicts(unresolvedFiles, mergeProvider)
|
||||
|
||||
for (file in resolvedFiles) {
|
||||
mergedFiles.add(file.getPath())
|
||||
}
|
||||
|
||||
if (resolvedFiles.size() == unresolvedFiles.size()) {
|
||||
break
|
||||
}
|
||||
else {
|
||||
unresolvedFiles.removeAll(mergedFiles)
|
||||
}
|
||||
}
|
||||
|
||||
repository.commit()
|
||||
|
||||
return mergeResult.result.toMutable().addChanged(mergedFiles)
|
||||
return resolveConflicts(mergeResult, repository)
|
||||
}
|
||||
else if (!mergeStatus.isSuccessful()) {
|
||||
throw IllegalStateException(mergeResult.toString())
|
||||
@@ -104,9 +100,7 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
fun fetch(prevRefUpdateResult: RefUpdate.Result? = null): Ref? {
|
||||
indicator?.checkCanceled()
|
||||
|
||||
val repository = manager.repository
|
||||
|
||||
val fetchResult = repository.fetch(remoteConfig, manager.credentialsProvider, indicator.asProgressMonitor()) ?: return null
|
||||
val fetchResult = manager.repository.fetch(remoteConfig, manager.credentialsProvider, indicator.asProgressMonitor()) ?: return null
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
printMessages(fetchResult)
|
||||
@@ -129,10 +123,10 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
// we can have more than one fetch ref spec, but currently we don't worry about it
|
||||
if (refUpdateResult == RefUpdate.Result.LOCK_FAILURE || refUpdateResult == RefUpdate.Result.IO_FAILURE) {
|
||||
if (prevRefUpdateResult == refUpdateResult) {
|
||||
throw IOException("Ref update result " + refUpdateResult.name() + ", we have already tried to fetch again, but no luck")
|
||||
throw IOException("Ref update result ${refUpdateResult.name()}, we have already tried to fetch again, but no luck")
|
||||
}
|
||||
|
||||
LOG.warn("Ref update result " + refUpdateResult.name() + ", trying again after 500 ms")
|
||||
LOG.warn("Ref update result ${refUpdateResult.name()}, trying again after 500 ms")
|
||||
Thread.sleep(500)
|
||||
return fetch(refUpdateResult)
|
||||
}
|
||||
@@ -165,17 +159,13 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
|
||||
val head = repository.getRef(Constants.HEAD) ?: throw NoHeadException(JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported)
|
||||
|
||||
// handle annotated tags
|
||||
val ref = repository.peel(unpeeledRef)
|
||||
val objectId = ref.getPeeledObjectId() ?: ref.getObjectId()
|
||||
// Check for FAST_FORWARD, ALREADY_UP_TO_DATE
|
||||
val revWalk = RevWalk(repository)
|
||||
var dirCacheCheckout: DirCacheCheckout? = null
|
||||
try {
|
||||
// handle annotated tags
|
||||
val ref = repository.peel(unpeeledRef)
|
||||
var objectId = ref.getPeeledObjectId()
|
||||
if (objectId == null) {
|
||||
objectId = ref.getObjectId()
|
||||
}
|
||||
|
||||
val srcCommit = revWalk.lookupCommit(objectId)
|
||||
val headId = head.getObjectId()
|
||||
if (headId == null) {
|
||||
@@ -235,11 +225,11 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
else {
|
||||
mergeMessage = commitMessageFormatter.mergeMessage(listOf(ref), head)
|
||||
repository.writeMergeCommitMsg(mergeMessage)
|
||||
repository.writeMergeHeads(arrayListOf(ref.getObjectId()))
|
||||
repository.writeMergeHeads(listOf(ref.getObjectId()))
|
||||
}
|
||||
val merger = mergeStrategy.newMerger(repository)
|
||||
val noProblems: Boolean
|
||||
var lowLevelResults: Map<String, org.eclipse.jgit.merge.MergeResult<*>>? = null
|
||||
var lowLevelResults: Map<String, org.eclipse.jgit.merge.MergeResult<out Sequence>>? = null
|
||||
var failingPaths: Map<String, ResolveMerger.MergeFailureReason>? = null
|
||||
var unmergedPaths: List<String>? = null
|
||||
if (merger is ResolveMerger) {
|
||||
@@ -297,7 +287,7 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
}
|
||||
}
|
||||
catch (e: org.eclipse.jgit.errors.CheckoutConflictException) {
|
||||
throw CheckoutConflictException(if (dirCacheCheckout == null) listOf<String>() else dirCacheCheckout.getConflicts(), e)
|
||||
throw CheckoutConflictException(dirCacheCheckout?.getConflicts() ?: listOf(), e)
|
||||
}
|
||||
finally {
|
||||
revWalk.close()
|
||||
@@ -305,7 +295,7 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
|
||||
}
|
||||
}
|
||||
|
||||
class MergeResultEx(val newHead: ObjectId?, val mergeStatus: MergeStatus, val mergedCommits: Array<ObjectId?>, val result: ImmutableUpdateResult, val conflicts: Map<String, org.eclipse.jgit.merge.MergeResult<*>>? = null)
|
||||
class MergeResultEx(val newHead: ObjectId?, val mergeStatus: MergeStatus, val mergedCommits: Array<ObjectId?>, val result: ImmutableUpdateResult, val conflicts: Map<String, org.eclipse.jgit.merge.MergeResult<out Sequence>>? = null)
|
||||
|
||||
private fun updateHead(refLogMessage: StringBuilder, newHeadId: ObjectId, oldHeadID: ObjectId, repository: Repository) {
|
||||
val refUpdate = repository.updateRef(Constants.HEAD)
|
||||
@@ -320,3 +310,37 @@ private fun updateHead(refLogMessage: StringBuilder, newHeadId: ObjectId, oldHea
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveConflicts(mergeResult: MergeResultEx, repository: Repository): MutableUpdateResult {
|
||||
assert(mergeResult.mergedCommits.size() == 2)
|
||||
val conflicts = mergeResult.conflicts!!
|
||||
val mergeProvider = JGitMergeProvider(repository, conflicts, { path, index ->
|
||||
val rawText = get(path)!!.getSequences().get(index) as RawText
|
||||
// RawText.EMPTY_TEXT if content is null - deleted
|
||||
if (rawText == RawText.EMPTY_TEXT) null else rawText.getContent()
|
||||
})
|
||||
val mergedFiles = resolveConflicts(mergeProvider, conflictsToVirtualFiles(conflicts), repository)
|
||||
return mergeResult.result.toMutable().addChanged(mergedFiles)
|
||||
}
|
||||
|
||||
private fun resolveConflicts(mergeProvider: JGitMergeProvider<out Any>, unresolvedFiles: MutableList<VirtualFile>, repository: Repository): List<String> {
|
||||
val resolvedFiles: List<VirtualFile>
|
||||
val mergedFiles = SmartList<String>()
|
||||
while (true) {
|
||||
resolvedFiles = resolveConflicts(unresolvedFiles, mergeProvider)
|
||||
|
||||
for (file in resolvedFiles) {
|
||||
mergedFiles.add(file.getPath())
|
||||
}
|
||||
|
||||
if (resolvedFiles.size() == unresolvedFiles.size()) {
|
||||
break
|
||||
}
|
||||
else {
|
||||
unresolvedFiles.removeAll(mergedFiles)
|
||||
}
|
||||
}
|
||||
|
||||
repository.commit()
|
||||
|
||||
return mergedFiles
|
||||
}
|
||||
|
||||
@@ -24,8 +24,9 @@ import org.jetbrains.settingsRepository.UpdateResult
|
||||
|
||||
class Reset(manager: GitRepositoryManager, indicator: ProgressIndicator) : Pull(manager, indicator) {
|
||||
fun reset(toTheirs: Boolean, localRepositoryInitializer: (() -> Unit)? = null): UpdateResult {
|
||||
val message = if (toTheirs) "Overwrite local to ${manager.getUpstream()}" else "Overwrite remote ${manager.getUpstream()} to local"
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Reset to ${if (toTheirs) "theirs" else "my"}")
|
||||
LOG.debug(message)
|
||||
}
|
||||
|
||||
val resetResult = repository.resetHard()
|
||||
@@ -33,7 +34,7 @@ class Reset(manager: GitRepositoryManager, indicator: ProgressIndicator) : Pull(
|
||||
|
||||
indicator?.checkCanceled()
|
||||
|
||||
val commitMessage = commitMessageFormatter.message(if (toTheirs) "Overwrite local to ${manager.getUpstream()}" else "Overwrite remote ${manager.getUpstream()} to local")
|
||||
val commitMessage = commitMessageFormatter.message(message)
|
||||
// grab added/deleted/renamed/modified files
|
||||
val mergeStrategy = if (toTheirs) MergeStrategy.THEIRS else MergeStrategy.OURS
|
||||
val firstMergeResult = pull(mergeStrategy, commitMessage)
|
||||
|
||||
@@ -59,7 +59,7 @@ class SyncManager(private val icsManager: IcsManager, private val autoSyncManage
|
||||
// well, we cannot commit? No problem, upcoming action must do something smart and solve the situation
|
||||
}
|
||||
catch (e: ProcessCanceledException) {
|
||||
LOG.debug("Canceled")
|
||||
LOG.warn("Canceled")
|
||||
return
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
@@ -104,7 +104,7 @@ class SyncManager(private val icsManager: IcsManager, private val autoSyncManage
|
||||
return
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
if (e !is AuthenticationException && e !is NoRemoteRepositoryException) {
|
||||
if (e !is AuthenticationException && e !is NoRemoteRepositoryException && e !is CannotResolveConflictInTestMode) {
|
||||
LOG.error(e)
|
||||
}
|
||||
exception = e
|
||||
@@ -188,4 +188,8 @@ enum class SyncType {
|
||||
MERGE,
|
||||
OVERWRITE_LOCAL,
|
||||
OVERWRITE_REMOTE
|
||||
}
|
||||
}
|
||||
|
||||
class NoRemoteRepositoryException(cause: Throwable) : RuntimeException(cause.getMessage(), cause)
|
||||
|
||||
class CannotResolveConflictInTestMode() : RuntimeException()
|
||||
@@ -17,9 +17,12 @@ package org.jetbrains.settingsRepository.test
|
||||
|
||||
import com.intellij.mock.MockVirtualFileSystem
|
||||
import com.intellij.openapi.components.RoamingType
|
||||
import com.intellij.openapi.components.impl.stores.StreamProvider
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vcs.merge.MergeSession
|
||||
import com.intellij.openapi.vfs.CharsetToolkit
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.testFramework.path
|
||||
import com.intellij.util.PathUtilRt
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.eclipse.jgit.api.Git
|
||||
@@ -28,16 +31,24 @@ import org.jetbrains.jgit.dirCache.AddFile
|
||||
import org.jetbrains.jgit.dirCache.deletePath
|
||||
import org.jetbrains.jgit.dirCache.edit
|
||||
import org.jetbrains.jgit.dirCache.writePath
|
||||
import org.jetbrains.settingsRepository.AM
|
||||
import org.jetbrains.settingsRepository.CannotResolveConflictInTestMode
|
||||
import org.jetbrains.settingsRepository.SyncType
|
||||
import org.jetbrains.settingsRepository.conflictResolver
|
||||
import org.jetbrains.settingsRepository.git.GitRepositoryManager
|
||||
import org.jetbrains.settingsRepository.git.commit
|
||||
import org.jetbrains.settingsRepository.git.computeIndexDiff
|
||||
import org.jetbrains.settingsRepository.git.resetHard
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.util.Arrays
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
// kotlin bug, cannot be val (.NoSuchMethodError: org.jetbrains.settingsRepository.SettingsRepositoryPackage.getMARKER_ACCEPT_MY()[B)
|
||||
object AM {
|
||||
val MARKER_ACCEPT_MY: ByteArray = "__accept my__".toByteArray()
|
||||
val MARKER_ACCEPT_THEIRS: ByteArray = "__accept theirs__".toByteArray()
|
||||
}
|
||||
|
||||
class GitTest : TestCase() {
|
||||
private val repositoryManager: GitRepositoryManager
|
||||
get() = icsManager.repositoryManager as GitRepositoryManager
|
||||
@@ -49,6 +60,28 @@ class GitTest : TestCase() {
|
||||
tempDirManager.createRepository("upstream")
|
||||
}
|
||||
|
||||
init {
|
||||
conflictResolver = { files, mergeProvider ->
|
||||
val mergeSession = mergeProvider.createMergeSession(files)
|
||||
for (file in files) {
|
||||
val mergeData = mergeProvider.loadRevisions(file)
|
||||
if (Arrays.equals(mergeData.CURRENT, AM.MARKER_ACCEPT_MY) || Arrays.equals(mergeData.LAST, AM.MARKER_ACCEPT_THEIRS)) {
|
||||
mergeSession.conflictResolvedForFile(file, MergeSession.Resolution.AcceptedYours)
|
||||
}
|
||||
else if (Arrays.equals(mergeData.CURRENT, AM.MARKER_ACCEPT_THEIRS) || Arrays.equals(mergeData.LAST, AM.MARKER_ACCEPT_MY)) {
|
||||
mergeSession.conflictResolvedForFile(file, MergeSession.Resolution.AcceptedTheirs)
|
||||
}
|
||||
else if (Arrays.equals(mergeData.LAST, AM.MARKER_ACCEPT_MY)) {
|
||||
file.setBinaryContent(mergeData.LAST!!)
|
||||
mergeProvider.conflictResolvedForFile(file)
|
||||
}
|
||||
else {
|
||||
throw CannotResolveConflictInTestMode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addAndCommit(path: String): FileInfo {
|
||||
val data = FileUtil.loadFileBytes(File(testDataPath, PathUtilRt.getFileName(path)))
|
||||
provider.save(path, data)
|
||||
@@ -284,15 +317,24 @@ class GitTest : TestCase() {
|
||||
Test fun `commit if unmerged`() {
|
||||
createLocalRepository(null)
|
||||
|
||||
provider.saveContent("remote.xml", "<foo />")
|
||||
|
||||
try {
|
||||
sync(SyncType.MERGE)
|
||||
}
|
||||
catch (e: CannotResolveConflictInTestMode) {
|
||||
}
|
||||
|
||||
// repository in unmerged state
|
||||
conflictResolver = {files, mergeProvider ->
|
||||
assertThat(files).hasSize(1)
|
||||
assertThat(files.first().path).isEqualTo("remote.xml")
|
||||
val mergeSession = mergeProvider.createMergeSession(files)
|
||||
mergeSession.conflictResolvedForFile(files.first(), MergeSession.Resolution.AcceptedTheirs)
|
||||
}
|
||||
sync(SyncType.MERGE)
|
||||
|
||||
provider.delete("remote.xml", RoamingType.PER_USER)
|
||||
repositoryManager.commit()
|
||||
|
||||
sync(SyncType.MERGE)
|
||||
restoreRemoteAfterPush()
|
||||
|
||||
compareFiles(fs())
|
||||
compareFiles(fs("remote.xml"))
|
||||
}
|
||||
|
||||
// remote is uninitialized (empty - initial commit is not done)
|
||||
@@ -364,4 +406,9 @@ class GitTest : TestCase() {
|
||||
private fun sync(syncType: SyncType) {
|
||||
icsManager.sync(syncType, fixtureManager.projectFixture.getProject())
|
||||
}
|
||||
}
|
||||
|
||||
fun StreamProvider.saveContent(fileSpec: String, content: String) {
|
||||
val data = content.toByteArray()
|
||||
saveContent(fileSpec, data, data.size(), RoamingType.PER_USER)
|
||||
}
|
||||
Reference in New Issue
Block a user