diff --git a/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsIgnoredHolderUpdateListener.java b/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsIgnoredHolderUpdateListener.java index 1797b70a4ade..2a3b900c5376 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsIgnoredHolderUpdateListener.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsIgnoredHolderUpdateListener.java @@ -1,18 +1,16 @@ // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.dvcs.ignore; -import com.intellij.dvcs.repo.AsyncFilesManagerListener; +import com.intellij.openapi.vcs.FilePath; import org.jetbrains.annotations.NotNull; -public interface VcsIgnoredHolderUpdateListener extends AsyncFilesManagerListener { +import java.util.Collection; +import java.util.EventListener; - default void updateStarted(@NotNull String ignorePath) {} +public interface VcsIgnoredHolderUpdateListener extends EventListener { - default void updateFinished(@NotNull String ignorePath) {} - - @Override default void updateStarted() {} - @Override - default void updateFinished() {} + default void updateFinished(@NotNull Collection ignoredPaths) {} + } diff --git a/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolder.java b/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolder.java index 5c58863d03c7..77f4892b2a6a 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolder.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolder.java @@ -5,7 +5,6 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; @@ -15,7 +14,7 @@ public interface VcsRepositoryIgnoredFilesHolder extends Disposable { void addUpdateStateListener(@NotNull VcsIgnoredHolderUpdateListener listener); - void startRescan(@Nullable String ignoreFilePath); + void startRescan(); boolean isInUpdateMode(); diff --git a/platform/dvcs-api/src/com/intellij/dvcs/repo/AsyncFilesManagerListener.java b/platform/dvcs-api/src/com/intellij/dvcs/repo/AsyncFilesManagerListener.java deleted file mode 100644 index e642a67a48c9..000000000000 --- a/platform/dvcs-api/src/com/intellij/dvcs/repo/AsyncFilesManagerListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 com.intellij.dvcs.repo; - -import com.intellij.openapi.vcs.changes.ChangeListManager; - -import java.util.EventListener; - -/** - *

- * The listener interface for handling async {@link ChangeListManager} node models updating. - *

- *

- * When model starts/finishes to update itself -> updateStarted/Finished will be called for all subscribers.
- * e.g. vcs updates its ignored file model after that we should trigger ui update in ChangesView;
- * moreover, when long duration update starts we should also trigger some action; - *

- */ -public interface AsyncFilesManagerListener extends EventListener { - - void updateStarted(); - - void updateFinished(); -} diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsIgnoredFilesHolderBase.kt b/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsIgnoredFilesHolderBase.kt index 017c652f461f..7ca1f8ff2714 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsIgnoredFilesHolderBase.kt +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsIgnoredFilesHolderBase.kt @@ -8,14 +8,11 @@ import com.intellij.openapi.vcs.changes.FileHolder import com.intellij.openapi.vcs.changes.VcsIgnoredFilesHolder import com.intellij.openapi.vcs.changes.VcsModifiableDirtyScope import com.intellij.openapi.vfs.VirtualFile -import com.intellij.util.EventDispatcher abstract class VcsIgnoredFilesHolderBase( private val repositoryManager: AbstractRepositoryManager ) : VcsIgnoredFilesHolder { - private val listeners = EventDispatcher.create(VcsIgnoredHolderUpdateListener::class.java) - private val vcsIgnoredHolderMap = repositoryManager.repositories.associateTo( hashMapOf()) { it to getHolder(it) } @@ -39,9 +36,7 @@ abstract class VcsIgnoredFilesHolderBase( override fun values() = vcsIgnoredHolderMap.flatMap { it.value.ignoredFiles } override fun startRescan() { - fireUpdateStarted() - vcsIgnoredHolderMap.values.forEach { it.startRescan(null) } - fireUpdateFinished() + vcsIgnoredHolderMap.values.forEach { it.startRescan() } } override fun cleanAll() { @@ -52,12 +47,4 @@ abstract class VcsIgnoredFilesHolderBase( repositoryManager.getRepositoryForFileQuick(file)?.let { repositoryForFile -> vcsIgnoredHolderMap[repositoryForFile] } - - private fun fireUpdateStarted() { - listeners.multicaster.updateStarted() - } - - private fun fireUpdateFinished() { - listeners.multicaster.updateFinished() - } } \ No newline at end of file diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolderBase.kt b/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolderBase.kt index ecf541221179..6d8765fc886b 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolderBase.kt +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ignore/VcsRepositoryIgnoredFilesHolderBase.kt @@ -2,16 +2,19 @@ package com.intellij.dvcs.ignore import com.intellij.dvcs.repo.Repository +import com.intellij.openapi.application.runReadAction import com.intellij.openapi.util.Disposer import com.intellij.openapi.vcs.FilePath -import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.newvfs.events.* import com.intellij.util.Alarm import com.intellij.util.EventDispatcher import com.intellij.util.ui.update.MergingUpdateQueue import com.intellij.util.ui.update.Update import com.intellij.vcsUtil.VcsUtil +import com.intellij.vfs.AsyncVfsEventsListener +import com.intellij.vfs.AsyncVfsEventsPostProcessor import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read @@ -22,7 +25,7 @@ abstract class VcsRepositoryIgnoredFilesHolderBase( protected val repository: REPOSITORY, updateQueueName: String, private val rescanIdentityName: String -) : VcsRepositoryIgnoredFilesHolder { +) : VcsRepositoryIgnoredFilesHolder, AsyncVfsEventsListener { private val inUpdateMode = AtomicBoolean(false) private val updateQueue = MergingUpdateQueue(updateQueueName, 500, true, null, this, null, Alarm.ThreadToUse.POOLED_THREAD) @@ -38,9 +41,13 @@ abstract class VcsRepositoryIgnoredFilesHolderBase( listeners.addListener(listener, this) } - override fun addFiles(files: Collection) { SET_LOCK.write { ignoredSet.addAll(files) } } + override fun addFiles(files: Collection) { + SET_LOCK.write { ignoredSet.addAll(files) } + } - override fun addFile(file: VirtualFile) { SET_LOCK.write { ignoredSet.add(file) } } + override fun addFile(file: VirtualFile) { + SET_LOCK.write { ignoredSet.add(file) } + } override fun isInUpdateMode() = inUpdateMode.get() @@ -57,64 +64,46 @@ abstract class VcsRepositoryIgnoredFilesHolderBase( } } - protected abstract fun requestIgnored(ignoreFilePath: String?): Set + override fun filesChanged(events: List) { + val affectedFiles = events + .asSequence() + .mapNotNull(::getAffectedFile) + .filter { repository.root == VcsUtil.getVcsRootFor(repository.project, it) } + .map(VcsUtil::getFilePath) + .toList() + + if (affectedFiles.isNotEmpty()) { + removeIgnoredFiles(affectedFiles) + checkIgnored(affectedFiles) + } + } + + fun setupVfsListener() = + runReadAction { + if (repository.project.isDisposed) return@runReadAction + AsyncVfsEventsPostProcessor.getInstance().addListener(this, this) + } + + protected abstract fun requestIgnored(paths: Collection? = null): Set protected abstract fun scanTurnedOff(): Boolean - override fun startRescan(ignoreFilePath: String?) { + override fun startRescan() { if (scanTurnedOff()) return - updateQueue.queue(object : Update(rescanIdentityName) { - override fun canEat(update: Update) = true - - override fun run() { - if (inUpdateMode.compareAndSet(false, true)) { - fireUpdateStarted() - doRescan() - inUpdateMode.set(false) - fireUpdateFinished() - } - } - }) - } - - private fun doRescan(ignoreFilePath: String? = null) { - val ignored = when { - ignoreFilePath == null -> requestIgnored(null) - isIgnoreFileUnderRepositoryRoot(ignoreFilePath)-> requestIgnored(ignoreFilePath) - else -> return - } - SET_LOCK.write { - ignoredSet.clear() - ignoredSet.addAll(ignored) + queueIgnoreUpdate(isFullRescan = true) { + doRescan() } } - private fun isIgnoreFileUnderRepositoryRoot(ignoreFilePath: String): Boolean { - return repository.root == VcsUtil.getVcsRootFor(repository.project, LocalFileSystem.getInstance().findFileByPath(ignoreFilePath)) - } - - private fun fireUpdateStarted(ignoreFilePath: String? = null) { - when (ignoreFilePath) { - null -> listeners.multicaster.updateStarted() - else -> listeners.multicaster.updateStarted(ignoreFilePath) - } - } - - private fun fireUpdateFinished(ignoreFilePath: String? = null) { - when (ignoreFilePath) { - null -> listeners.multicaster.updateFinished() - else -> listeners.multicaster.updateFinished(ignoreFilePath) - } - } - - override fun removeIgnoredFiles(files: Collection): List { + override fun removeIgnoredFiles(filePaths: Collection): MutableList { val removedIgnoredFiles = arrayListOf() + val filePathsSet = filePaths.toHashSet() SET_LOCK.write { val iter = ignoredSet.iterator() while (iter.hasNext()) { val filePath = VcsUtil.getFilePath(iter.next()) - if (files.contains(filePath)) { + if (isUnder(filePathsSet, filePath)) { iter.remove() removedIgnoredFiles.add(filePath) } @@ -122,4 +111,77 @@ abstract class VcsRepositoryIgnoredFilesHolderBase( } return removedIgnoredFiles } + + private fun checkIgnored(paths: Collection) { + if (scanTurnedOff()) return + + queueIgnoreUpdate(isFullRescan = false) { + doCheckIgnored(paths) + } + } + + private fun queueIgnoreUpdate(isFullRescan: Boolean, action: () -> Set) { + updateQueue.queue(object : Update(rescanIdentityName) { + override fun canEat(update: Update) = isFullRescan + + override fun run() { + if (inUpdateMode.compareAndSet(false, true)) { + fireUpdateStarted() + val ignored = action() + inUpdateMode.set(false) + fireUpdateFinished(ignored) + } + } + }) + } + + private fun doCheckIgnored(paths: Collection): Set { + val ignored = requestIgnored(paths) + addNotContainedIgnores(ignored) + return ignored.map(VcsUtil::getFilePath).toSet() + } + + private fun addNotContainedIgnores(ignored: Set) = + SET_LOCK.write { + ignored.forEach { ignored -> + if (!isUnder(ignoredSet, ignored)) { + ignoredSet.add(ignored) + } + } + } + + private fun doRescan(): Set { + val ignored = requestIgnored() + SET_LOCK.write { + ignoredSet.clear() + ignoredSet.addAll(ignored) + } + return ignored.map(VcsUtil::getFilePath).toSet() + } + + private fun fireUpdateStarted() { + listeners.multicaster.updateStarted() + } + + private fun fireUpdateFinished(paths: Collection) { + listeners.multicaster.updateFinished(paths) + } + + private fun isUnder(parents: Set, child: VirtualFile) = generateSequence(child) { it.parent }.any { it in parents } + private fun isUnder(parents: Set, child: FilePath) = generateSequence(child) { it.parentPath }.any { it in parents } + + companion object { + @JvmStatic + fun getAffectedFile(event: VFileEvent): VirtualFile? = + runReadAction { + when { + event is VFileCreateEvent && event.parent.isValid -> event.file + event is VFileDeleteEvent || event is VFileMoveEvent || event.isRename() -> event.file + event is VFileCopyEvent && event.newParent.isValid -> event.newParent.findChild(event.newChildName) + else -> null + } + } + + private fun VFileEvent.isRename() = this is VFilePropertyChangeEvent && propertyName == VirtualFile.PROP_NAME + } } diff --git a/plugins/git4idea/src/git4idea/commands/Git.java b/plugins/git4idea/src/git4idea/commands/Git.java index 337c9f976414..3e5e747769e8 100644 --- a/plugins/git4idea/src/git4idea/commands/Git.java +++ b/plugins/git4idea/src/git4idea/commands/Git.java @@ -18,6 +18,7 @@ package git4idea.commands; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; +import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import git4idea.branch.GitRebaseParams; @@ -71,7 +72,10 @@ public interface Git { GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners); @NotNull - Set ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable VirtualFile path) throws VcsException; + Set ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable Collection paths) throws VcsException; + + @NotNull + Set ignoredFilesNoChunk(@NotNull Project project, @NotNull VirtualFile root, @Nullable List paths) throws VcsException; @NotNull Set untrackedFiles(@NotNull Project project, @NotNull VirtualFile root, diff --git a/plugins/git4idea/src/git4idea/commands/GitImpl.java b/plugins/git4idea/src/git4idea/commands/GitImpl.java index cc04aac775e1..17a7df8a3d7c 100644 --- a/plugins/git4idea/src/git4idea/commands/GitImpl.java +++ b/plugins/git4idea/src/git4idea/commands/GitImpl.java @@ -20,6 +20,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -74,13 +75,30 @@ public class GitImpl extends GitImplBase { @NotNull @Override - public Set ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable VirtualFile path) + public Set ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable Collection paths) + throws VcsException { + Set ignoredFiles = new HashSet<>(); + + if (paths == null) { + ignoredFiles.addAll(ignoredFilesNoChunk(project, root, null)); + } + else { + for (List relativePaths : VcsFileUtil.chunkPaths(root, paths)) { + ignoredFiles.addAll(ignoredFilesNoChunk(project, root, relativePaths)); + } + } + return ignoredFiles; + } + + @NotNull + @Override + public Set ignoredFilesNoChunk(@NotNull Project project, @NotNull VirtualFile root, @Nullable List paths) throws VcsException { GitLineHandler h = new GitLineHandler(project, root, GitCommand.STATUS); h.setSilent(true); h.addParameters("--ignored", "--porcelain", "-z"); - if (path != null) { - h.addParameters(path.getPath()); + if (paths != null) { + h.addParameters(paths); } h.endOptions(); diff --git a/plugins/git4idea/src/git4idea/ignore/GitIgnoredFilesHolder.kt b/plugins/git4idea/src/git4idea/ignore/GitIgnoredFilesHolder.kt index e931fb0b3845..febc201773a4 100644 --- a/plugins/git4idea/src/git4idea/ignore/GitIgnoredFilesHolder.kt +++ b/plugins/git4idea/src/git4idea/ignore/GitIgnoredFilesHolder.kt @@ -24,7 +24,7 @@ class GitIgnoredFilesHolder(val project: Project, val manager: GitRepositoryMana override fun createHolder() = GitIgnoredFilesHolder(project, manager) override fun refresh(project: Project) { - manager.repositories.forEach { r -> r.ignoredFilesHolder.startRescan(null) } + manager.repositories.forEach { r -> r.ignoredFilesHolder.startRescan() } } } } \ No newline at end of file diff --git a/plugins/git4idea/src/git4idea/ignore/GitRepositoryIgnoredFilesHolder.kt b/plugins/git4idea/src/git4idea/ignore/GitRepositoryIgnoredFilesHolder.kt index a6959df93c25..4027cf6364fb 100644 --- a/plugins/git4idea/src/git4idea/ignore/GitRepositoryIgnoredFilesHolder.kt +++ b/plugins/git4idea/src/git4idea/ignore/GitRepositoryIgnoredFilesHolder.kt @@ -4,26 +4,17 @@ package git4idea.ignore import com.intellij.dvcs.ignore.VcsRepositoryIgnoredFilesHolderBase import com.intellij.openapi.project.Project import com.intellij.openapi.util.registry.Registry -import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vcs.FilePath import git4idea.commands.Git import git4idea.repo.GitRepository -import java.util.* class GitRepositoryIgnoredFilesHolder(private val project: Project, repository: GitRepository, private val git: Git) : VcsRepositoryIgnoredFilesHolderBase(repository, "GitIgnoreUpdate", "gitRescanIgnored") { - override fun requestIgnored(ignoreFilePath: String?) = - HashSet(git.ignoredFiles(project, repository.root, findIgnoreFileVcsRootOrDefault(ignoreFilePath))) - - private fun findIgnoreFileVcsRootOrDefault(ignoreFilePath: String?) = - if (ignoreFilePath != null) { - LocalFileSystem.getInstance().findFileByPath(ignoreFilePath)?.parent ?: repository.root - } - else { - repository.root - } + override fun requestIgnored(paths: Collection?) = + HashSet(git.ignoredFiles(project, repository.root, paths)) override fun scanTurnedOff() = !Registry.`is`("git.process.ignored") } \ No newline at end of file diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java index 463cb8539fe8..bea57c2ebe6e 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java @@ -7,13 +7,14 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.progress.util.BackgroundTaskUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.changes.ChangesViewI; import com.intellij.openapi.vcs.changes.ChangesViewManager; -import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; -import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.util.StopWatch; +import com.intellij.vcsUtil.VcsFileUtil; import git4idea.GitLocalBranch; import git4idea.GitUtil; import git4idea.GitVcs; @@ -84,6 +85,7 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository { GitRepositoryImpl repository = new GitRepositoryImpl(root, gitDir, project, project, !listenToRepoChanges); if (listenToRepoChanges) { repository.getUntrackedFilesHolder().setupVfsListener(project); + repository.getIgnoredFilesHolder().setupVfsListener(); repository.setupUpdater(); notifyListenersAsync(repository); } @@ -94,7 +96,7 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository { GitRepositoryUpdater updater = new GitRepositoryUpdater(this, myRepositoryFiles); Disposer.register(this, updater); if (myIgnoredRepositoryFilesHolder != null) { - myIgnoredRepositoryFilesHolder.startRescan(null); + myIgnoredRepositoryFilesHolder.startRescan(); } } @@ -266,43 +268,24 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository { private static class MyRepositoryIgnoredHolderUpdateListener implements VcsIgnoredHolderUpdateListener { @NotNull private final ChangesViewI myChangesViewI; - @NotNull private final VcsDirtyScopeManager myDirtyScopeManager; + @NotNull private final Project myProject; MyRepositoryIgnoredHolderUpdateListener(@NotNull Project project) { myChangesViewI = ChangesViewManager.getInstance(project); - myDirtyScopeManager = VcsDirtyScopeManager.getInstance(project); - } - - @Override - public void updateStarted(@NotNull String gitIgnorePath) { - myChangesViewI.scheduleRefresh(); //TODO optimize: remove additional refresh + myProject = project; } @Override public void updateStarted() { - myChangesViewI.scheduleRefresh(); + myChangesViewI.scheduleRefresh(); //TODO optimize: remove additional refresh } @Override - public void updateFinished(@NotNull String gitIgnorePath) { - markGitignoreContainingFolderAsDirty(gitIgnorePath); + public void updateFinished(@NotNull Collection ignoredPaths) { + if(myProject.isDisposed()) return; + + VcsFileUtil.markFilesDirty(myProject, ContainerUtil.newArrayList(ignoredPaths)); myChangesViewI.scheduleRefresh(); } - - @Override - public void updateFinished() { - myChangesViewI.scheduleRefresh(); - } - - private void markGitignoreContainingFolderAsDirty(@NotNull String gitIgnorePath) { - VirtualFile gitIgnore = LocalFileSystem.getInstance().findFileByPath(gitIgnorePath); - if (gitIgnore != null) { - VirtualFile gitIgnoreParent = gitIgnore.getParent(); - if (gitIgnoreParent != null) { - myDirtyScopeManager.dirDirtyRecursively(gitIgnoreParent); - } - } - } - } } diff --git a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java index bbea05034d9b..bb6c83409b8b 100644 --- a/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java +++ b/plugins/git4idea/src/git4idea/repo/GitUntrackedFilesHolder.java @@ -17,7 +17,6 @@ package git4idea.repo; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.ProjectLevelVcsManager; @@ -25,7 +24,7 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.newvfs.events.*; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.vfs.AsyncVfsEventsListener; import com.intellij.vfs.AsyncVfsEventsPostProcessor; import git4idea.GitLocalBranch; @@ -36,6 +35,8 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import static com.intellij.dvcs.ignore.VcsRepositoryIgnoredFilesHolderBase.getAffectedFile; + /** *

* Stores files which are untracked by the Git repository. @@ -295,35 +296,13 @@ public class GitUntrackedFilesHolder implements Disposable, AsyncVfsEventsListen boolean excludeChanged = myRepositoryFiles.isExclude(path); if(gitIgnoreChanged || excludeChanged){ - rescanIgnoredFiles(path); + rescanIgnoredFiles(); } return gitIgnoreChanged || excludeChanged; } - private void rescanIgnoredFiles(@NotNull String gitIgnorePath) { //TODO move to ignore manager - myRepository.getIgnoredFilesHolder().startRescan(gitIgnorePath); - } - - @Nullable - private static VirtualFile getAffectedFile(@NotNull VFileEvent event) { - return ReadAction.compute(() -> { - if (event instanceof VFileCreateEvent) { - return ((VFileCreateEvent)event).getParent().isValid() ? event.getFile() : null; - } - else if (event instanceof VFileDeleteEvent || event instanceof VFileMoveEvent || isRename(event)) { - return event.getFile(); - } - else if (event instanceof VFileCopyEvent) { - VFileCopyEvent copyEvent = (VFileCopyEvent) event; - VirtualFile newParent = copyEvent.getNewParent(); - return newParent.isValid() ? newParent.findChild(copyEvent.getNewChildName()) : null; - } - return null; - }); - } - - private static boolean isRename(@NotNull VFileEvent event) { - return event instanceof VFilePropertyChangeEvent && ((VFilePropertyChangeEvent)event).getPropertyName().equals(VirtualFile.PROP_NAME); + private void rescanIgnoredFiles() { //TODO move to ignore manager + myRepository.getIgnoredFilesHolder().startRescan(); } private boolean notIgnored(@Nullable VirtualFile file) { diff --git a/plugins/git4idea/src/git4idea/vfs/GitVFSListener.java b/plugins/git4idea/src/git4idea/vfs/GitVFSListener.java index d8a583557285..778c1774ba01 100644 --- a/plugins/git4idea/src/git4idea/vfs/GitVFSListener.java +++ b/plugins/git4idea/src/git4idea/vfs/GitVFSListener.java @@ -9,7 +9,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.ObjectsConvertor; import com.intellij.openapi.vcs.VcsException; @@ -112,12 +111,7 @@ public class GitVFSListener extends VcsVFSListener { List files = e.getValue(); pi.setText(root.getPresentableUrl()); try { - Set untrackedForRepo = myGit.untrackedFiles(myProject, root, files); - if (Registry.is("git.process.ignored")) { - Collection ignoredForRepo = ContainerUtil.subtract(files, untrackedForRepo); - getIgnoreRepoHolder(root).addFiles(ignoredForRepo); - } - retainedFiles.addAll(untrackedForRepo); + retainedFiles.addAll(myGit.untrackedFiles(myProject, root, files)); } catch (VcsException ex) { myVcsConsoleWriter.showMessage(ex.getMessage()); @@ -195,7 +189,6 @@ public class GitVFSListener extends VcsVFSListener { @Override public void execute(@NotNull VirtualFile root, @NotNull List files) throws VcsException { GitFileUtils.deletePaths(myProject, root, files, "--ignore-unmatch", "--cached"); - getIgnoreRepoHolder(root).removeIgnoredFiles(files); if (!myProject.isDisposed()) { VcsFileUtil.markFilesDirty(myProject, files); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVFSListener.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVFSListener.java index 06087bf4bccb..6070e2d1ab02 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgVFSListener.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgVFSListener.java @@ -99,8 +99,6 @@ public class HgVFSListener extends VcsVFSListener { Collection untrackedForRepo = new HgStatusCommand.Builder(false).unknown(true).removed(true).build(myProject) .getFiles(repo, new ArrayList<>(files)); untrackedFiles.addAll(untrackedForRepo); - List ignoredForRepo = ContainerUtil.filter(files, file -> !untrackedForRepo.contains(file)); - getIgnoreRepoHolder(repo).addFiles(ignoredForRepo); } addedFiles.retainAll(untrackedFiles); // select files to add if there is something to select diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgStatusCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgStatusCommand.java index 86c1b6e2dfd8..d44561999232 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgStatusCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgStatusCommand.java @@ -238,8 +238,13 @@ public class HgStatusCommand { @NotNull public Collection getFiles(@NotNull VirtualFile repo, @Nullable List files) { + return getFiles(repo, files != null ? ObjectsConvertor.vf2fp(files): null); + } + + @NotNull + public Collection getFiles(@NotNull VirtualFile repo, @Nullable Collection paths) { Collection resultFiles = new HashSet<>(); - Set change = executeInCurrentThread(repo, files != null ? ObjectsConvertor.vf2fp(files) : null); + Set change = executeInCurrentThread(repo, paths); for (HgChange hgChange : change) { resultFiles.add(hgChange.afterFile().toFilePath().getVirtualFile()); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgIgnoredFileHolder.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgIgnoredFileHolder.java index a1b2ece6dee3..e49736954063 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgIgnoredFileHolder.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgIgnoredFileHolder.java @@ -60,7 +60,7 @@ public class HgIgnoredFileHolder extends VcsIgnoredFilesHolderBase @Override public void refresh(Project project) { - myManager.getRepositories().forEach(r -> r.getIgnoredFilesHolder().startRescan(null)); + myManager.getRepositories().forEach(r -> r.getIgnoredFilesHolder().startRescan()); } } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgLocalIgnoredHolder.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgLocalIgnoredHolder.java index 14acaf6a716f..fc518cde2f9b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgLocalIgnoredHolder.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgLocalIgnoredHolder.java @@ -17,6 +17,7 @@ package org.zmlx.hg4idea.provider; import com.intellij.dvcs.ignore.VcsRepositoryIgnoredFilesHolderBase; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -24,6 +25,7 @@ import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.command.HgStatusCommand; import org.zmlx.hg4idea.repo.HgRepository; +import java.util.Collection; import java.util.Set; public class HgLocalIgnoredHolder extends VcsRepositoryIgnoredFilesHolderBase { @@ -34,10 +36,10 @@ public class HgLocalIgnoredHolder extends VcsRepositoryIgnoredFilesHolderBase requestIgnored(@Nullable String ignoreFilePath) { + protected Set requestIgnored(@Nullable Collection paths) { Set ignored = ContainerUtil.newHashSet(); ignored.addAll(new HgStatusCommand.Builder(false).ignored(true).build(repository.getProject()) - .getFiles(repository.getRoot(), null)); + .getFiles(repository.getRoot(), paths != null ? ContainerUtil.newArrayList(paths) : null)); return ignored; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java index 9c9d38a2d19c..ed868195f36d 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java @@ -11,12 +11,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.changes.ChangesViewI; import com.intellij.openapi.vcs.changes.ChangesViewManager; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.Hash; +import com.intellij.vcsUtil.VcsFileUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgNameWithHashInfo; @@ -53,6 +55,7 @@ public class HgRepositoryImpl extends RepositoryImpl implements HgRepository { myReader = new HgRepositoryReader(vcs, VfsUtilCore.virtualToIoFile(myHgDir)); myConfig = HgConfig.getInstance(getProject(), rootDir); myLocalIgnoredHolder = new HgLocalIgnoredHolder(this); + myLocalIgnoredHolder.setupVfsListener(); Disposer.register(this, myLocalIgnoredHolder); myLocalIgnoredHolder.addUpdateStateListener(new MyIgnoredHolderAsyncListener(getProject())); update(); @@ -73,7 +76,7 @@ public class HgRepositoryImpl extends RepositoryImpl implements HgRepository { private void setupUpdater() { HgRepositoryUpdater updater = new HgRepositoryUpdater(this); Disposer.register(this, updater); - myLocalIgnoredHolder.startRescan(null); + myLocalIgnoredHolder.startRescan(); } @NotNull @@ -254,9 +257,11 @@ public class HgRepositoryImpl extends RepositoryImpl implements HgRepository { private static class MyIgnoredHolderAsyncListener implements VcsIgnoredHolderUpdateListener { @NotNull private final ChangesViewI myChangesViewI; + @NotNull private final Project myProject; MyIgnoredHolderAsyncListener(@NotNull Project project) { myChangesViewI = ChangesViewManager.getInstance(project); + myProject = project; } @Override @@ -265,7 +270,10 @@ public class HgRepositoryImpl extends RepositoryImpl implements HgRepository { } @Override - public void updateFinished() { + public void updateFinished(@NotNull Collection ignoredPaths) { + if(myProject.isDisposed()) return; + + VcsFileUtil.markFilesDirty(myProject, ContainerUtil.newArrayList(ignoredPaths)); myChangesViewI.scheduleRefresh(); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryUpdater.java b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryUpdater.java index aebf4c0f0f20..a778c04c1f61 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryUpdater.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryUpdater.java @@ -166,7 +166,7 @@ final class HgRepositoryUpdater implements Disposable, BulkFileListener { }); } if (dirstateFileChanged || hgIgnoreChanged) { - myRepository.getIgnoredFilesHolder().startRescan(null); + myRepository.getIgnoredFilesHolder().startRescan(); final VirtualFile root = myRepository.getRoot(); myDirtyScopeManager.dirDirtyRecursively(root); if (dirstateFileChanged) {