mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-118140 don't add unversioned file to VCS on file move
The bug was introduced in783963bwhile fixing IDEA-153272. Originally the code used to avoid registering unversioned and ignored moved files in performMoveRename(), thus they were not passed to `git add/rm` in executeMoveRename(), thus they were correctly not added to the VCS. But that behavior led to IDEA-153272 (both files are lost when unversioned file is moved to overwrite existing file of the same name) Here is what actually happened: 1. Delete original file to let it be overwritten. 2. beforeFileDeleted: remember to git rm this file later. 3. Move unversioned file to the place of original. 4. Don't remember the file because it is unversioned. 5. git rm the original file from executeDelete() after the command finished. => both unversioned (during the move) and original (during git rm) were deleted. The fix in783963bwas to avoid #4 in this sequence. However, moved unversioned files were not filtered anymore, and thus were later passed to executeMoveRename and further to `git add/rm/mv`. This fix is actually a partial revert of783963b: it returns filtering by UNKNOWN/INGORED file status back (with a special handle for Perforce), but in order to keep IDEA-153272 fixed it moves the unversioned & ignored check from performMoveRename to executeMoveRename. The latter is called after files are checked for doNotDeleteAddedCopiedOrMovedFiles => file is not scheduled for deletion anymore even if it was unversioned moved file. Tests were added for both IDEA-153272 and IDEA-118140.
This commit is contained in:
@@ -266,6 +266,10 @@ public abstract class VcsVFSListener implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean filterOutUnknownFiles() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void processMovedFile(VirtualFile file, String newParentPath, String newName) {
|
||||
final FileStatus status = FileStatusManager.getInstance(myProject).getStatus(file);
|
||||
LOG.debug("Checking moved file " + file + "; status=" + status);
|
||||
@@ -292,7 +296,10 @@ public abstract class VcsVFSListener implements Disposable {
|
||||
final List<MovedFileInfo> movedFiles = new ArrayList<MovedFileInfo>(myMovedFiles);
|
||||
LOG.debug("executeMoveRename " + movedFiles);
|
||||
myMovedFiles.clear();
|
||||
performMoveRename(movedFiles);
|
||||
performMoveRename(ContainerUtil.filter(movedFiles, info -> {
|
||||
FileStatus status = FileStatusManager.getInstance(myProject).getStatus(info.myFile);
|
||||
return !(status == FileStatus.UNKNOWN && filterOutUnknownFiles()) && status != FileStatus.IGNORED;
|
||||
}));
|
||||
}
|
||||
|
||||
protected VcsDeleteType needConfirmDeletion(final VirtualFile file) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.ide.highlighter.ProjectFileType
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl
|
||||
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
@@ -39,7 +40,7 @@ abstract class VcsPlatformTest : PlatformTestCase() {
|
||||
|
||||
private lateinit var myTestStartedIndicator: String
|
||||
|
||||
protected lateinit var changeListManager: ChangeListManager
|
||||
protected lateinit var changeListManager: ChangeListManagerImpl
|
||||
|
||||
@Throws(Exception::class)
|
||||
override fun setUp() {
|
||||
@@ -56,7 +57,7 @@ abstract class VcsPlatformTest : PlatformTestCase() {
|
||||
myProjectRoot = myProject.baseDir
|
||||
myProjectPath = myProjectRoot.path
|
||||
|
||||
changeListManager = ChangeListManager.getInstance(myProject)
|
||||
changeListManager = ChangeListManager.getInstance(myProject) as ChangeListManagerImpl
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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 git4idea.checkin
|
||||
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.command.CommandProcessor
|
||||
import com.intellij.openapi.vcs.VcsConfiguration
|
||||
import com.intellij.openapi.vcs.VcsConfiguration.StandardConfirmation.ADD
|
||||
import com.intellij.openapi.vcs.VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY
|
||||
import com.intellij.openapi.vcs.VcsShowConfirmationOption.Value.DO_NOTHING_SILENTLY
|
||||
import com.intellij.openapi.vcs.VcsTestUtil
|
||||
import com.intellij.openapi.vcs.VcsTestUtil.renameFileInCommand
|
||||
import com.intellij.openapi.vcs.VcsVFSListener
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.testFramework.runInEdtAndWait
|
||||
import com.intellij.testFramework.vcs.AbstractVcsTestCase.setStandardConfirmation
|
||||
import com.intellij.vcsUtil.VcsUtil
|
||||
import git4idea.GitUtil.getLogString
|
||||
import git4idea.GitVcs
|
||||
import git4idea.test.GitExecutor.*
|
||||
import git4idea.test.GitSingleRepoTest
|
||||
import java.io.File
|
||||
|
||||
class GitMoveTest : GitSingleRepoTest() {
|
||||
|
||||
override fun getDebugLogCategories() = super.getDebugLogCategories().plus("#" + VcsVFSListener::class.java.name)
|
||||
|
||||
fun `test unchanged file should be added to Git on move`() {
|
||||
ADD.doNothing()
|
||||
val file = "before.txt"
|
||||
echo(file, "some\ncontent\nere")
|
||||
addCommit("created $file")
|
||||
|
||||
val vf = LocalFileSystem.getInstance().refreshAndFindFileByPath(myProjectPath + "/$file")!!
|
||||
|
||||
renameFile(vf, "ver-ren.txt")
|
||||
assertTrue("File should versioned! All changes: " + getLogString(myProjectPath, changeListManager.allChanges),
|
||||
!changeListManager.isUnversioned(vf))
|
||||
val change = changeListManager.getChange(vf)!!
|
||||
assertTrue("Change should be rename: " + change, change.isRenamed)
|
||||
}
|
||||
|
||||
// IDEA-153272
|
||||
fun `test move unversioned file over existing file should keep the file`() {
|
||||
ADD.doNothing()
|
||||
val content = "original content"
|
||||
val fileName = "file.txt"
|
||||
val originalDir = myProjectRoot.createDir("original")
|
||||
val unversionedDir = myProjectRoot.createDir("unv")
|
||||
|
||||
val original = originalDir.createFile(fileName, content)
|
||||
val unversioned = unversionedDir.createFile(fileName, content)
|
||||
val originalFile = File(original.path)
|
||||
val unversionedFile = File(unversioned.path)
|
||||
|
||||
git("add original/$fileName")
|
||||
git("commit -m msg")
|
||||
updateChangeListManager()
|
||||
|
||||
runInEdtAndWait {
|
||||
CommandProcessor.getInstance().executeCommand(myProject, {
|
||||
runWriteAction {
|
||||
original.delete(this)
|
||||
}
|
||||
runWriteAction {
|
||||
unversioned.move(this, original.parent)
|
||||
}
|
||||
}, null, null)
|
||||
}
|
||||
updateChangeListManager()
|
||||
|
||||
assertTrue("Original file should exist", originalFile.exists()) // IDEA-153272 failed here: both files were deleted.
|
||||
assertFalse("Unversioned file shouldn't exist", unversionedFile.exists())
|
||||
updateChangeListManager()
|
||||
val change = changeListManager.getChange(VcsUtil.getFilePath(originalFile))
|
||||
assertNull("There should be no change for $originalFile. Changes: ${getLogString(myProjectPath, changeListManager.allChanges)}", change)
|
||||
}
|
||||
|
||||
// IDEA-118140
|
||||
fun `test unversioned file should not be added to Git on move`() {
|
||||
ADD.doNothing()
|
||||
val file = prepareUnversionedFile("unv.txt")
|
||||
|
||||
renameFile(file, "unv-ren.txt")
|
||||
assertUnversioned(file)
|
||||
}
|
||||
|
||||
fun `test unversioned file should not be added to Git on move even if add silently`() {
|
||||
ADD.doNothing()
|
||||
val file = prepareUnversionedFile("unv.txt")
|
||||
|
||||
ADD.doSilently()
|
||||
renameFile(file, "unv-ren.txt")
|
||||
assertUnversioned(file)
|
||||
}
|
||||
|
||||
private fun VcsConfiguration.StandardConfirmation.doSilently() = setStandardConfirmation(myProject, GitVcs.NAME, this, DO_ACTION_SILENTLY)
|
||||
private fun VcsConfiguration.StandardConfirmation.doNothing() = setStandardConfirmation(myProject, GitVcs.NAME, this, DO_NOTHING_SILENTLY)
|
||||
|
||||
private fun prepareUnversionedFile(fileName: String): VirtualFile {
|
||||
val file = myProjectRoot.createFile(fileName, "initial\ncontent\n")
|
||||
updateChangeListManager()
|
||||
assertUnversioned(file)
|
||||
return file
|
||||
}
|
||||
|
||||
private fun VirtualFile.createDir(dir: String) = VcsTestUtil.findOrCreateDir(myProject, this, dir)
|
||||
private fun VirtualFile.createFile(fileName: String, content: String) = VcsTestUtil.createFile(myProject, this, fileName, content)
|
||||
|
||||
private fun renameFile(file: VirtualFile, newName: String) {
|
||||
renameFileInCommand(myProject, file, newName)
|
||||
updateChangeListManager()
|
||||
}
|
||||
|
||||
private fun assertUnversioned(file: VirtualFile) {
|
||||
assertTrue("File should be unversioned! All changes: " + getLogString(myProjectPath, changeListManager.allChanges),
|
||||
changeListManager.isUnversioned(file))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user