vcs-ignore: ability to check and populate VCS ignored file holders without full repository rescan

This commit is contained in:
Dmitry Zhuravlev
2018-12-28 13:33:14 +03:00
parent 588be6ce89
commit d174ede815
18 changed files with 190 additions and 200 deletions
@@ -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<FilePath> ignoredPaths) {}
}
@@ -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();
@@ -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;
/**
* <p>
* The listener interface for handling async {@link ChangeListManager} node models updating.
* </p>
* <p>
* When model starts/finishes to update itself -> updateStarted/Finished will be called for all subscribers.<br/>
* e.g. vcs updates its ignored file model after that we should trigger ui update in ChangesView;<br/>
* moreover, when long duration update starts we should also trigger some action;
* </p>
*/
public interface AsyncFilesManagerListener extends EventListener {
void updateStarted();
void updateFinished();
}
@@ -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<REPOSITORY : Repository>(
private val repositoryManager: AbstractRepositoryManager<REPOSITORY>
) : VcsIgnoredFilesHolder {
private val listeners = EventDispatcher.create(VcsIgnoredHolderUpdateListener::class.java)
private val vcsIgnoredHolderMap =
repositoryManager.repositories.associateTo(
hashMapOf<REPOSITORY, VcsRepositoryIgnoredFilesHolder>()) { it to getHolder(it) }
@@ -39,9 +36,7 @@ abstract class VcsIgnoredFilesHolderBase<REPOSITORY : Repository>(
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<REPOSITORY : Repository>(
repositoryManager.getRepositoryForFileQuick(file)?.let { repositoryForFile ->
vcsIgnoredHolderMap[repositoryForFile]
}
private fun fireUpdateStarted() {
listeners.multicaster.updateStarted()
}
private fun fireUpdateFinished() {
listeners.multicaster.updateFinished()
}
}
@@ -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<REPOSITORY : Repository>(
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<REPOSITORY : Repository>(
listeners.addListener(listener, this)
}
override fun addFiles(files: Collection<VirtualFile>) { SET_LOCK.write { ignoredSet.addAll(files) } }
override fun addFiles(files: Collection<VirtualFile>) {
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<REPOSITORY : Repository>(
}
}
protected abstract fun requestIgnored(ignoreFilePath: String?): Set<VirtualFile>
override fun filesChanged(events: List<VFileEvent>) {
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<FilePath>? = null): Set<VirtualFile>
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<FilePath>): List<FilePath> {
override fun removeIgnoredFiles(filePaths: Collection<FilePath>): MutableList<FilePath> {
val removedIgnoredFiles = arrayListOf<FilePath>()
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<REPOSITORY : Repository>(
}
return removedIgnoredFiles
}
private fun checkIgnored(paths: Collection<FilePath>) {
if (scanTurnedOff()) return
queueIgnoreUpdate(isFullRescan = false) {
doCheckIgnored(paths)
}
}
private fun queueIgnoreUpdate(isFullRescan: Boolean, action: () -> Set<FilePath>) {
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<FilePath>): Set<FilePath> {
val ignored = requestIgnored(paths)
addNotContainedIgnores(ignored)
return ignored.map(VcsUtil::getFilePath).toSet()
}
private fun addNotContainedIgnores(ignored: Set<VirtualFile>) =
SET_LOCK.write {
ignored.forEach { ignored ->
if (!isUnder(ignoredSet, ignored)) {
ignoredSet.add(ignored)
}
}
}
private fun doRescan(): Set<FilePath> {
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<FilePath>) {
listeners.multicaster.updateFinished(paths)
}
private fun isUnder(parents: Set<VirtualFile>, child: VirtualFile) = generateSequence(child) { it.parent }.any { it in parents }
private fun isUnder(parents: Set<FilePath>, 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
}
}
@@ -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<VirtualFile> ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable VirtualFile path) throws VcsException;
Set<VirtualFile> ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable Collection<FilePath> paths) throws VcsException;
@NotNull
Set<VirtualFile> ignoredFilesNoChunk(@NotNull Project project, @NotNull VirtualFile root, @Nullable List<String> paths) throws VcsException;
@NotNull
Set<VirtualFile> untrackedFiles(@NotNull Project project, @NotNull VirtualFile root,
@@ -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<VirtualFile> ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable VirtualFile path)
public Set<VirtualFile> ignoredFiles(@NotNull Project project, @NotNull VirtualFile root, @Nullable Collection<FilePath> paths)
throws VcsException {
Set<VirtualFile> ignoredFiles = new HashSet<>();
if (paths == null) {
ignoredFiles.addAll(ignoredFilesNoChunk(project, root, null));
}
else {
for (List<String> relativePaths : VcsFileUtil.chunkPaths(root, paths)) {
ignoredFiles.addAll(ignoredFilesNoChunk(project, root, relativePaths));
}
}
return ignoredFiles;
}
@NotNull
@Override
public Set<VirtualFile> ignoredFilesNoChunk(@NotNull Project project, @NotNull VirtualFile root, @Nullable List<String> 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();
@@ -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() }
}
}
}
@@ -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<GitRepository>(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<FilePath>?) =
HashSet(git.ignoredFiles(project, repository.root, paths))
override fun scanTurnedOff() = !Registry.`is`("git.process.ignored")
}
@@ -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<FilePath> 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);
}
}
}
}
}
@@ -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;
/**
* <p>
* 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) {
@@ -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<VirtualFile> files = e.getValue();
pi.setText(root.getPresentableUrl());
try {
Set<VirtualFile> untrackedForRepo = myGit.untrackedFiles(myProject, root, files);
if (Registry.is("git.process.ignored")) {
Collection<VirtualFile> 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<FilePath> files) throws VcsException {
GitFileUtils.deletePaths(myProject, root, files, "--ignore-unmatch", "--cached");
getIgnoreRepoHolder(root).removeIgnoredFiles(files);
if (!myProject.isDisposed()) {
VcsFileUtil.markFilesDirty(myProject, files);
}
@@ -99,8 +99,6 @@ public class HgVFSListener extends VcsVFSListener {
Collection<VirtualFile> untrackedForRepo = new HgStatusCommand.Builder(false).unknown(true).removed(true).build(myProject)
.getFiles(repo, new ArrayList<>(files));
untrackedFiles.addAll(untrackedForRepo);
List<VirtualFile> 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
@@ -238,8 +238,13 @@ public class HgStatusCommand {
@NotNull
public Collection<VirtualFile> getFiles(@NotNull VirtualFile repo, @Nullable List<VirtualFile> files) {
return getFiles(repo, files != null ? ObjectsConvertor.vf2fp(files): null);
}
@NotNull
public Collection<VirtualFile> getFiles(@NotNull VirtualFile repo, @Nullable Collection<FilePath> paths) {
Collection<VirtualFile> resultFiles = new HashSet<>();
Set<HgChange> change = executeInCurrentThread(repo, files != null ? ObjectsConvertor.vf2fp(files) : null);
Set<HgChange> change = executeInCurrentThread(repo, paths);
for (HgChange hgChange : change) {
resultFiles.add(hgChange.afterFile().toFilePath().getVirtualFile());
}
@@ -60,7 +60,7 @@ public class HgIgnoredFileHolder extends VcsIgnoredFilesHolderBase<HgRepository>
@Override
public void refresh(Project project) {
myManager.getRepositories().forEach(r -> r.getIgnoredFilesHolder().startRescan(null));
myManager.getRepositories().forEach(r -> r.getIgnoredFilesHolder().startRescan());
}
}
}
@@ -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<HgRepository> {
@@ -34,10 +36,10 @@ public class HgLocalIgnoredHolder extends VcsRepositoryIgnoredFilesHolderBase<Hg
@NotNull
@Override
protected Set<VirtualFile> requestIgnored(@Nullable String ignoreFilePath) {
protected Set<VirtualFile> requestIgnored(@Nullable Collection<? extends FilePath> paths) {
Set<VirtualFile> 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;
}
@@ -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<FilePath> ignoredPaths) {
if(myProject.isDisposed()) return;
VcsFileUtil.markFilesDirty(myProject, ContainerUtil.newArrayList(ignoredPaths));
myChangesViewI.scheduleRefresh();
}
}
@@ -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) {