IDEA-143759 Repository contains unmerged paths. Merge not work for me

This commit is contained in:
Vladimir Krivosheev
2015-08-25 16:26:57 +02:00
parent 84218d4ec2
commit 1e7009197f
4 changed files with 78 additions and 47 deletions
@@ -50,9 +50,11 @@ public interface RepositoryManager {
public fun processChildren(path: String, filter: (name: String) -> Boolean, processor: (name: String, inputStream: InputStream) -> Boolean)
/**
* Not all implementations support progress indicator (will not be updated on progress)
* Not all implementations support progress indicator (will not be updated on progress).
*
* syncType will be passed if called before sync.
*/
public fun commit(indicator: ProgressIndicator? = null): Boolean
public fun commit(indicator: ProgressIndicator? = null, syncType: SyncType? = null): Boolean
public fun getAheadCommitsCount(): Int
@@ -80,12 +82,6 @@ public interface RepositoryManager {
}
}
fun RepositoryManager.commitIfCan(indicator: ProgressIndicator? = null) {
if (canCommit()) {
commit(indicator)
}
}
public interface UpdateResult {
val changed: Collection<String>
val deleted: Collection<String>
@@ -24,10 +24,12 @@ import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.SmartList
import org.eclipse.jgit.api.AddCommand
import org.eclipse.jgit.api.errors.UnmergedPathsException
import org.eclipse.jgit.errors.TransportException
import org.eclipse.jgit.lib.ConfigConstants
import org.eclipse.jgit.lib.Constants
import org.eclipse.jgit.lib.Repository
import org.eclipse.jgit.lib.RepositoryState
import org.eclipse.jgit.storage.file.FileRepositoryBuilder
import org.eclipse.jgit.transport.*
import org.jetbrains.jgit.dirCache.AddLoadedFile
@@ -110,7 +112,37 @@ class GitRepositoryManager(private val credentialsStore: NotNullLazyValue<Creden
repository.deletePath(path, isFile, false)
}
override fun commit(indicator: ProgressIndicator?) = lock.write { commit(this, indicator) }
override fun commit(indicator: ProgressIndicator?, syncType: SyncType?): Boolean {
lock.write {
try {
// will be reset if OVERWRITE_LOCAL, so, we should not fix state in this case
return commitIfCan(indicator, if (syncType == SyncType.OVERWRITE_LOCAL) repository.getRepositoryState() else repository.fixAndGetState())
}
catch (e: UnmergedPathsException) {
if (syncType == SyncType.OVERWRITE_LOCAL) {
LOG.warn("Unmerged detected, ignored because sync type is OVERWRITE_LOCAL", e)
return false
}
else {
indicator?.checkCanceled()
LOG.warn("Unmerged detected, will be attempted to resolve", e)
resolveUnmergedConflicts(repository)
indicator?.checkCanceled()
return commitIfCan(indicator, repository.fixAndGetState())
}
}
}
}
private fun commitIfCan(indicator: ProgressIndicator?, state: RepositoryState): Boolean {
if (state.canCommit()) {
return commit(this, indicator)
}
else {
LOG.warn("Cannot commit, repository in state ${state.getDescription()}")
return false
}
}
override fun getAheadCommitsCount() = repository.getAheadCommitsCount()
+40 -37
View File
@@ -55,28 +55,10 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
LOG.debug("Pull")
val repository = manager.repository
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 state = manager.repository.fixAndGetState()
if (!state.canCheckout()) {
LOG.error("Cannot pull, repository in state ${state.getDescription()}")
return null
}
var refToMerge = prefetchedRefToMerge ?: fetch() ?: return null
@@ -87,7 +69,7 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
}
if (mergeStatus == MergeStatus.CONFLICTING) {
return resolveConflicts(mergeResult, repository)
return resolveConflicts(mergeResult, manager.repository)
}
else if (!mergeStatus.isSuccessful()) {
throw IllegalStateException(mergeResult.toString())
@@ -180,7 +162,7 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
if (refUpdate.update() != RefUpdate.Result.NEW) {
throw NoHeadException(JGitText.get().commitOnRepoWithoutHEADCurrentlyNotSupported)
}
return MergeResultEx(srcCommit, MergeStatus.FAST_FORWARD, arrayOf<ObjectId?>(null, srcCommit), ImmutableUpdateResult(dirCacheCheckout.getUpdated().keySet(), dirCacheCheckout.getRemoved()))
return MergeResultEx(MergeStatus.FAST_FORWARD, arrayOf<ObjectId?>(null, srcCommit), ImmutableUpdateResult(dirCacheCheckout.getUpdated().keySet(), dirCacheCheckout.getRemoved()))
}
val refLogMessage = StringBuilder("merge ")
@@ -188,7 +170,7 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
val headCommit = revWalk.lookupCommit(headId)
if (!forceMerge && revWalk.isMergedInto(srcCommit, headCommit)) {
return MergeResultEx(headCommit, MergeStatus.ALREADY_UP_TO_DATE, arrayOf<ObjectId?>(headCommit, srcCommit), EMPTY_UPDATE_RESULT)
return MergeResultEx(MergeStatus.ALREADY_UP_TO_DATE, arrayOf<ObjectId?>(headCommit, srcCommit), EMPTY_UPDATE_RESULT)
//return MergeResult(headCommit, srcCommit, array(headCommit, srcCommit), MergeStatus.ALREADY_UP_TO_DATE, mergeStrategy, null)
}
else if (!forceMerge && fastForwardMode != FastForwardMode.NO_FF && revWalk.isMergedInto(headCommit, srcCommit)) {
@@ -197,24 +179,21 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
dirCacheCheckout = DirCacheCheckout(repository, headCommit.getTree(), repository.lockDirCache(), srcCommit.getTree())
dirCacheCheckout.setFailOnConflict(true)
dirCacheCheckout.checkout()
val newHead: ObjectId
val mergeStatus: MergeStatus
if (squash) {
newHead = headId
mergeStatus = MergeStatus.FAST_FORWARD_SQUASHED
val squashedCommits = RevWalkUtils.find(revWalk, srcCommit, headCommit)
repository.writeSquashCommitMsg(SquashMessageFormatter().format(squashedCommits, head))
}
else {
updateHead(refLogMessage, srcCommit, headId, repository)
newHead = srcCommit
mergeStatus = MergeStatus.FAST_FORWARD
}
return MergeResultEx(newHead, mergeStatus, arrayOf<ObjectId?>(headCommit, srcCommit), ImmutableUpdateResult(dirCacheCheckout.getUpdated().keySet(), dirCacheCheckout.getRemoved()))
return MergeResultEx(mergeStatus, arrayOf<ObjectId?>(headCommit, srcCommit), ImmutableUpdateResult(dirCacheCheckout.getUpdated().keySet(), dirCacheCheckout.getRemoved()))
}
else {
if (fastForwardMode == FastForwardMode.FF_ONLY) {
return MergeResultEx(headCommit, MergeStatus.ABORTED, arrayOf<ObjectId?>(headCommit, srcCommit), EMPTY_UPDATE_RESULT)
return MergeResultEx(MergeStatus.ABORTED, arrayOf<ObjectId?>(headCommit, srcCommit), EMPTY_UPDATE_RESULT)
}
val mergeMessage: String
@@ -257,7 +236,6 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
result = ImmutableUpdateResult(dirCacheCheckout.getUpdated().keySet(), dirCacheCheckout.getRemoved())
}
var newHeadId: ObjectId? = null
var mergeStatus: MergeResult.MergeStatus? = null
if (!commit && squash) {
mergeStatus = MergeResult.MergeStatus.MERGED_SQUASHED_NOT_COMMITTED
@@ -266,23 +244,22 @@ open class Pull(val manager: GitRepositoryManager, val indicator: ProgressIndica
mergeStatus = MergeResult.MergeStatus.MERGED_NOT_COMMITTED
}
if (commit && !squash) {
newHeadId = repository.commit(commitMessage, refLogMessage.toString()).getId()
repository.commit(commitMessage, refLogMessage.toString()).getId()
mergeStatus = MergeResult.MergeStatus.MERGED
}
if (commit && squash) {
newHeadId = headCommit.getId()
mergeStatus = MergeResult.MergeStatus.MERGED_SQUASHED
}
return MergeResultEx(newHeadId, mergeStatus!!, arrayOf(headCommit.getId(), srcCommit.getId()), result!!)
return MergeResultEx(mergeStatus!!, arrayOf(headCommit.getId(), srcCommit.getId()), result!!)
}
else if (failingPaths == null) {
repository.writeMergeCommitMsg(MergeMessageFormatter().formatWithConflicts(mergeMessage, unmergedPaths))
return MergeResultEx(null, MergeResult.MergeStatus.CONFLICTING, arrayOf(headCommit.getId(), srcCommit.getId()), result!!, lowLevelResults)
return MergeResultEx(MergeResult.MergeStatus.CONFLICTING, arrayOf(headCommit.getId(), srcCommit.getId()), result!!, lowLevelResults)
}
else {
repository.writeMergeCommitMsg(null)
repository.writeMergeHeads(null)
return MergeResultEx(null, MergeResult.MergeStatus.FAILED, arrayOf(headCommit.getId(), srcCommit.getId()), result!!, lowLevelResults)
return MergeResultEx(MergeResult.MergeStatus.FAILED, arrayOf(headCommit.getId(), srcCommit.getId()), result!!, lowLevelResults)
}
}
}
@@ -295,7 +272,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<out Sequence>>? = null)
class MergeResultEx(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)
@@ -340,7 +317,33 @@ private fun resolveConflicts(mergeProvider: JGitMergeProvider<out Any>, unresolv
}
}
// merge commit template will be used, so, we don't have to specify commit message explicitly
repository.commit()
return mergedFiles
}
private fun Repository.fixAndGetState(): RepositoryState {
var state = getRepositoryState()
if (state == RepositoryState.MERGING) {
resolveUnmergedConflicts(this)
// compute new state
state = getRepositoryState()
}
return state
}
private fun resolveUnmergedConflicts(repository: Repository) {
val conflicts = LinkedHashMap<String, Array<ByteArray?>>()
repository.newObjectReader().use { reader ->
val dirCache = repository.readDirCache()
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)
}
+1 -1
View File
@@ -55,7 +55,7 @@ class SyncManager(private val icsManager: IcsManager, private val autoSyncManage
if (localRepositoryInitializer == null) {
try {
// we commit before even if sync "RESET_TO_THEIRS" — preserve history and ability to undo
repositoryManager.commitIfCan(indicator)
repositoryManager.commit(indicator, syncType)
// well, we cannot commit? No problem, upcoming action must do something smart and solve the situation
}
catch (e: ProcessCanceledException) {