mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
vcs-ignore: implement ignored files highlighting for Git (IDEA-116425)
This commit is contained in:
@@ -1266,6 +1266,9 @@ git.use.setsid.for.native.ssh.description=Wrap 'git' process with 'setsid' for r
|
||||
This fixes issue with SSH_ASKPASS if IDE is executed from terminal or under DM/WM that pass system TTY from Xorg to GUI applications.
|
||||
git.use.push.force.with.lease=true
|
||||
git.use.push.force.with.lease.description=Use '--force-with-lease' to push from IDE instead of '--force'
|
||||
git.process.ignored=false
|
||||
git.process.ignored.description=Process and highlight Git ignored files.
|
||||
|
||||
|
||||
hg4idea.process.ignored=true
|
||||
hg4idea.process.ignored.description=Process and highlight mercurial ignored files. \
|
||||
|
||||
@@ -507,10 +507,13 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
|
||||
Set<? super VirtualFile> refreshFiles) {
|
||||
while (iterator.hasNext()) {
|
||||
VirtualFile file = iterator.next().getVirtualFile();
|
||||
if (file != null && isIgnoredFile(file)) {
|
||||
iterator.remove();
|
||||
fileHolder.addFile(file);
|
||||
refreshFiles.add(file);
|
||||
if (file != null && isVcsIgnoredFile(file)) {
|
||||
AbstractVcs vcs = VcsUtil.getVcsFor(myProject, file);
|
||||
if (vcs != null) {
|
||||
iterator.remove();
|
||||
fileHolder.addFile(vcs, file);
|
||||
refreshFiles.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -566,7 +569,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
|
||||
takeChanges = myUpdateException == null;
|
||||
}
|
||||
if (takeChanges) {
|
||||
// update IDEA-level ignored files
|
||||
// update vcs ignored files
|
||||
updateIgnoredFiles(dataHolder.getComposite());
|
||||
}
|
||||
|
||||
@@ -647,7 +650,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
|
||||
// do actual requests about file statuses
|
||||
Getter<Boolean> disposedGetter = () -> myProject.isDisposed() || myUpdater.isStopped();
|
||||
final UpdatingChangeListBuilder builder = new UpdatingChangeListBuilder(updater,
|
||||
dataHolder.getComposite(), disposedGetter, this);
|
||||
dataHolder.getComposite(), disposedGetter);
|
||||
|
||||
for (final VcsDirtyScope scope : scopes) {
|
||||
indicator.checkCanceled();
|
||||
@@ -1505,7 +1508,7 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
|
||||
scheduleUnversionedUpdate();
|
||||
}
|
||||
|
||||
private void updateIgnoredFiles(final FileHolderComposite composite) {
|
||||
private void updateIgnoredFiles(FileHolderComposite composite) {
|
||||
final VirtualFileHolder vfHolder = composite.getVFHolder(FileHolder.HolderType.UNVERSIONED);
|
||||
final List<VirtualFile> unversionedFiles = vfHolder.getFiles();
|
||||
exchangeWithIgnored(composite, vfHolder, unversionedFiles);
|
||||
@@ -1516,10 +1519,14 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
|
||||
}
|
||||
|
||||
private void exchangeWithIgnored(FileHolderComposite composite, VirtualFileHolder vfHolder, List<? extends VirtualFile> unversionedFiles) {
|
||||
IgnoredFilesCompositeHolder ignoredFileHolder = composite.getIgnoredFileHolder();
|
||||
for (VirtualFile file : unversionedFiles) {
|
||||
if (isIgnoredFile(file)) {
|
||||
vfHolder.removeFile(file);
|
||||
composite.getIgnoredFileHolder().addFile(file);
|
||||
if (ignoredFileHolder.containsFile(file)) {
|
||||
AbstractVcs vcs = VcsUtil.getVcsFor(myProject, file);
|
||||
if (vcs != null) {
|
||||
vfHolder.removeFile(file);
|
||||
ignoredFileHolder.addFile(vcs, file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-13
@@ -13,7 +13,6 @@ import java.util.*;
|
||||
|
||||
public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
private final Map<AbstractVcs, IgnoredFilesHolder> myVcsIgnoredHolderMap;
|
||||
private IgnoredFilesHolder myIdeIgnoredFilesHolder;
|
||||
private final Project myProject;
|
||||
private final ProjectLevelVcsManager myVcsManager;
|
||||
|
||||
@@ -21,7 +20,6 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
super();
|
||||
myProject = project;
|
||||
myVcsIgnoredHolderMap = new HashMap<>();
|
||||
myIdeIgnoredFilesHolder = new RecursiveFileHolder(myProject, HolderType.IGNORED);
|
||||
myVcsManager = ProjectLevelVcsManager.getInstance(myProject);
|
||||
}
|
||||
|
||||
@@ -29,7 +27,6 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
public void cleanAll() {
|
||||
myVcsIgnoredHolderMap.values().forEach(IgnoredFilesHolder::cleanAll);
|
||||
myVcsIgnoredHolderMap.clear();
|
||||
myIdeIgnoredFilesHolder.cleanAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,7 +35,6 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
if (myVcsIgnoredHolderMap.containsKey(vcs)) {
|
||||
myVcsIgnoredHolderMap.get(vcs).cleanAndAdjustScope(scope);
|
||||
}
|
||||
myIdeIgnoredFilesHolder.cleanAndAdjustScope(scope);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,7 +43,6 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
for (Map.Entry<AbstractVcs, IgnoredFilesHolder> entry : myVcsIgnoredHolderMap.entrySet()) {
|
||||
result.myVcsIgnoredHolderMap.put(entry.getKey(), (IgnoredFilesHolder)entry.getValue().copy());
|
||||
}
|
||||
result.myIdeIgnoredFilesHolder = (IgnoredFilesHolder)myIdeIgnoredFilesHolder.copy();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -56,10 +51,6 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
return HolderType.IGNORED;
|
||||
}
|
||||
|
||||
public void addFile(@NotNull VirtualFile file) {
|
||||
myIdeIgnoredFilesHolder.addFile(file);
|
||||
}
|
||||
|
||||
public void addFile(@NotNull AbstractVcs vcs, @NotNull VirtualFile file) {
|
||||
myVcsIgnoredHolderMap.get(vcs).addFile(file);
|
||||
}
|
||||
@@ -70,7 +61,6 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
}
|
||||
|
||||
public boolean containsFile(@NotNull VirtualFile file) {
|
||||
if (myIdeIgnoredFilesHolder.containsFile(file)) return true;
|
||||
final AbstractVcs vcs = myVcsManager.getVcsFor(file);
|
||||
if (vcs == null) return false;
|
||||
final IgnoredFilesHolder ignoredFilesHolder = myVcsIgnoredHolderMap.get(vcs);
|
||||
@@ -80,7 +70,6 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
@NotNull
|
||||
public Collection<VirtualFile> values() {
|
||||
final HashSet<VirtualFile> result = ContainerUtil.newHashSet();
|
||||
result.addAll(myIdeIgnoredFilesHolder.values());
|
||||
result.addAll(StreamEx.of(myVcsIgnoredHolderMap.values()).flatCollection(IgnoredFilesHolder::values).toSet());
|
||||
return result;
|
||||
}
|
||||
@@ -113,11 +102,11 @@ public class IgnoredFilesCompositeHolder implements FileHolder {
|
||||
return false;
|
||||
}
|
||||
IgnoredFilesCompositeHolder other = (IgnoredFilesCompositeHolder)obj;
|
||||
return myVcsIgnoredHolderMap.equals(other.myVcsIgnoredHolderMap) && myIdeIgnoredFilesHolder.equals(other.myIdeIgnoredFilesHolder);
|
||||
return myVcsIgnoredHolderMap.equals(other.myVcsIgnoredHolderMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(myVcsIgnoredHolderMap, myIdeIgnoredFilesHolder);
|
||||
return Objects.hash(myVcsIgnoredHolderMap);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-16
@@ -23,7 +23,6 @@ class UpdatingChangeListBuilder implements ChangelistBuilder {
|
||||
private final ChangeListUpdater myChangeListUpdater;
|
||||
private final FileHolderComposite myComposite;
|
||||
private final Getter<Boolean> myDisposedGetter;
|
||||
private final ChangeListManager myChangeListManager;
|
||||
private final ProjectLevelVcsManager myVcsManager;
|
||||
|
||||
private VcsDirtyScope myScope;
|
||||
@@ -31,14 +30,12 @@ class UpdatingChangeListBuilder implements ChangelistBuilder {
|
||||
|
||||
private Factory<JComponent> myAdditionalInfo;
|
||||
|
||||
UpdatingChangeListBuilder(final ChangeListUpdater changeListUpdater,
|
||||
final FileHolderComposite composite,
|
||||
final Getter<Boolean> disposedGetter,
|
||||
final ChangeListManager changeListManager) {
|
||||
UpdatingChangeListBuilder(ChangeListUpdater changeListUpdater,
|
||||
FileHolderComposite composite,
|
||||
Getter<Boolean> disposedGetter) {
|
||||
myChangeListUpdater = changeListUpdater;
|
||||
myComposite = composite;
|
||||
myDisposedGetter = disposedGetter;
|
||||
myChangeListManager = changeListManager;
|
||||
myVcsManager = ProjectLevelVcsManager.getInstance(changeListUpdater.getProject());
|
||||
}
|
||||
|
||||
@@ -60,7 +57,7 @@ class UpdatingChangeListBuilder implements ChangelistBuilder {
|
||||
public void processChangeInList(Change change, @Nullable ChangeList changeList, VcsKey vcsKey) {
|
||||
checkIfDisposed();
|
||||
|
||||
LOG.debug("[processChangeInList-1] entering, cl name: " + ((changeList == null) ? null: changeList.getName()) +
|
||||
LOG.debug("[processChangeInList-1] entering, cl name: " + ((changeList == null) ? null : changeList.getName()) +
|
||||
" change: " + ChangesUtil.getFilePath(change).getPath());
|
||||
final String fileName = ChangesUtil.getFilePath(change).getName();
|
||||
if (FileTypeManager.getInstance().isFileIgnored(fileName)) {
|
||||
@@ -104,15 +101,7 @@ class UpdatingChangeListBuilder implements ChangelistBuilder {
|
||||
@Override
|
||||
public void processUnversionedFile(VirtualFile file) {
|
||||
if (acceptFile(file, false)) {
|
||||
if (myChangeListManager.isIgnoredFile(file)) {
|
||||
myComposite.getIgnoredFileHolder().addFile(file);
|
||||
}
|
||||
else if (myComposite.getIgnoredFileHolder().containsFile(file)) {
|
||||
// does not need to add: parent dir is already added
|
||||
}
|
||||
else {
|
||||
myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).addFile(file);
|
||||
}
|
||||
myComposite.getVFHolder(FileHolder.HolderType.UNVERSIONED).addFile(file);
|
||||
// if a file was previously marked as switched through recursion, remove it from switched list
|
||||
myComposite.getSwitchedFileHolder().removeFile(file);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ public interface VcsIgnoredFilesHolder extends IgnoredFilesHolder {
|
||||
|
||||
default boolean isInUpdatingMode() {return false;}
|
||||
|
||||
default void startRescan(){}
|
||||
|
||||
interface Provider {
|
||||
@NotNull
|
||||
AbstractVcs getVcs();
|
||||
|
||||
@@ -190,6 +190,8 @@
|
||||
<vcsRepositoryInitializer implementation="git4idea.repo.GitRepositoryInitializer"/>
|
||||
<openapi.vcs.changes.actions.diff.ChangeDiffRequestProvider
|
||||
implementation="git4idea.diff.GitSubmoduleDiffRequestProvider"/>
|
||||
<vcsChangesViewRefresher implementation="git4idea.ignore.GitIgnoredFilesHolder$Provider"/>
|
||||
<vcs.ignoredFilesHolder implementation="git4idea.ignore.GitIgnoredFilesHolder$Provider"/>
|
||||
|
||||
<projectService serviceImplementation="git4idea.repo.GitRepositoryManager"/>
|
||||
<projectService serviceImplementation="git4idea.ui.branch.GitBranchManager"/>
|
||||
|
||||
@@ -70,6 +70,9 @@ public interface Git {
|
||||
@NotNull
|
||||
GitCommandResult init(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLineHandlerListener... listeners);
|
||||
|
||||
@NotNull
|
||||
Set<VirtualFile> ignoredFiles(@NotNull Project project, @NotNull VirtualFile root) throws VcsException;
|
||||
|
||||
@NotNull
|
||||
Set<VirtualFile> untrackedFiles(@NotNull Project project, @NotNull VirtualFile root,
|
||||
@Nullable Collection<VirtualFile> files) throws VcsException;
|
||||
|
||||
@@ -48,8 +48,6 @@ import static java.util.Collections.*;
|
||||
/**
|
||||
* Easy-to-use wrapper of common native Git commands.
|
||||
* Most of them return result as {@link GitCommandResult}.
|
||||
*
|
||||
* @author Kirill Likhodedov
|
||||
*/
|
||||
public class GitImpl extends GitImplBase {
|
||||
|
||||
@@ -74,6 +72,40 @@ public class GitImpl extends GitImplBase {
|
||||
return runCommand(h);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<VirtualFile> ignoredFiles(@NotNull Project project, @NotNull VirtualFile root) throws VcsException {
|
||||
GitLineHandler h = new GitLineHandler(project, root, GitCommand.STATUS);
|
||||
h.setSilent(true);
|
||||
h.addParameters("--ignored", "--porcelain", "-z");
|
||||
h.endOptions();
|
||||
|
||||
final String output = runCommand(h).getOutputOrThrow();
|
||||
return parseLSFilesOutput(root, output, "!! ");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Set<VirtualFile> parseLSFilesOutput(@NotNull VirtualFile root, String output, @NotNull String fileStatusPrefix) {
|
||||
if (StringUtil.isEmptyOrSpaces(output)) return emptySet();
|
||||
|
||||
final Set<VirtualFile> ignoredFiles = new HashSet<>();
|
||||
for (String relPath : output.split("\u0000")) {
|
||||
if (!fileStatusPrefix.isEmpty() && !relPath.startsWith(fileStatusPrefix)) continue;
|
||||
|
||||
VirtualFile f = root.findFileByRelativePath(relPath.substring(fileStatusPrefix.length()));
|
||||
if (f == null) {
|
||||
// files was created on disk, but VirtualFile hasn't yet been created,
|
||||
// when the GitChangeProvider has already been requested about changes.
|
||||
LOG.info(String.format("VirtualFile for path [%s] is null", relPath));
|
||||
}
|
||||
else {
|
||||
ignoredFiles.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
return ignoredFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Queries Git for the unversioned files in the given paths. </p>
|
||||
* <p>Ignored files are left ignored, i. e. no information is returned about them (thus this method may also be used as a
|
||||
@@ -108,7 +140,6 @@ public class GitImpl extends GitImplBase {
|
||||
@NotNull VirtualFile root,
|
||||
@Nullable List<String> relativePaths)
|
||||
throws VcsException {
|
||||
final Set<VirtualFile> untrackedFiles = new HashSet<>();
|
||||
GitLineHandler h = new GitLineHandler(project, root, GitCommand.LS_FILES);
|
||||
h.setSilent(true);
|
||||
h.addParameters("--exclude-standard", "--others", "-z");
|
||||
@@ -118,23 +149,7 @@ public class GitImpl extends GitImplBase {
|
||||
}
|
||||
|
||||
final String output = runCommand(h).getOutputOrThrow();
|
||||
if (StringUtil.isEmptyOrSpaces(output)) {
|
||||
return untrackedFiles;
|
||||
}
|
||||
|
||||
for (String relPath : output.split("\u0000")) {
|
||||
VirtualFile f = root.findFileByRelativePath(relPath);
|
||||
if (f == null) {
|
||||
// files was created on disk, but VirtualFile hasn't yet been created,
|
||||
// when the GitChangeProvider has already been requested about changes.
|
||||
LOG.info(String.format("VirtualFile for path [%s] is null", relPath));
|
||||
}
|
||||
else {
|
||||
untrackedFiles.add(f);
|
||||
}
|
||||
}
|
||||
|
||||
return untrackedFiles;
|
||||
return parseLSFilesOutput(root, output, "");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 git4idea.ignore
|
||||
|
||||
import com.intellij.dvcs.repo.AsyncFilesManagerListener
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.AbstractVcs
|
||||
import com.intellij.openapi.vcs.changes.ChangesViewRefresher
|
||||
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
|
||||
import com.intellij.vcsUtil.VcsUtil
|
||||
import git4idea.GitUtil
|
||||
import git4idea.GitVcs
|
||||
import git4idea.repo.GitRepository
|
||||
import git4idea.repo.GitRepositoryManager
|
||||
|
||||
class GitIgnoredFilesHolder(val project: Project, val manager: GitRepositoryManager) : VcsIgnoredFilesHolder {
|
||||
|
||||
private val listeners = EventDispatcher.create(AsyncFilesManagerListener::class.java)
|
||||
|
||||
private val vcsIgnoredHolderMap =
|
||||
manager.repositories.associateTo(hashMapOf<GitRepository, GitRepositoryIgnoredHolder>()) { it to it.ignoredFilesHolder }
|
||||
|
||||
override fun isInUpdatingMode() = vcsIgnoredHolderMap.values.any(GitRepositoryIgnoredHolder::isInUpdateMode)
|
||||
|
||||
override fun notifyVcsStarted(scope: AbstractVcs<*>?) {}
|
||||
|
||||
override fun addFile(file: VirtualFile) {
|
||||
findIgnoreHolderByFile(file)?.addFile(file)
|
||||
}
|
||||
|
||||
override fun containsFile(file: VirtualFile) = findIgnoreHolderByFile(file)?.containsFile(file) ?: false
|
||||
|
||||
override fun values() = vcsIgnoredHolderMap.flatMap { it.value.getIgnoredFiles() }
|
||||
|
||||
override fun startRescan() {
|
||||
fireUpdateStarted()
|
||||
vcsIgnoredHolderMap.values.forEach { it.startRescan() }
|
||||
fireUpdateFinished()
|
||||
}
|
||||
|
||||
override fun cleanAll() {
|
||||
vcsIgnoredHolderMap.clear()
|
||||
}
|
||||
|
||||
override fun copy() = GitIgnoredFilesHolder(project, manager)
|
||||
|
||||
override fun getType() = FileHolder.HolderType.IGNORED
|
||||
|
||||
override fun cleanAndAdjustScope(scope: VcsModifiableDirtyScope) {}
|
||||
|
||||
private fun findIgnoreHolderByFile(file: VirtualFile): GitRepositoryIgnoredHolder? {
|
||||
val repositoryRoot = VcsUtil.getVcsRootFor(project, file) ?: return null
|
||||
val repositoryForRoot = manager.getRepositoryForRoot(repositoryRoot) ?: return null
|
||||
return vcsIgnoredHolderMap[repositoryForRoot]
|
||||
}
|
||||
|
||||
private fun fireUpdateStarted() {
|
||||
listeners.multicaster.updateStarted()
|
||||
}
|
||||
|
||||
private fun fireUpdateFinished() {
|
||||
listeners.multicaster.updateFinished()
|
||||
}
|
||||
|
||||
class Provider(val project: Project, val manager: GitRepositoryManager) : VcsIgnoredFilesHolder.Provider, ChangesViewRefresher {
|
||||
|
||||
private val gitVcs = GitVcs.getInstance(project)
|
||||
|
||||
override fun getVcs() = gitVcs
|
||||
|
||||
override fun createHolder() = GitIgnoredFilesHolder(project, manager)
|
||||
|
||||
override fun refresh(project: Project) {
|
||||
GitUtil.getRepositoryManager(project).repositories.forEach { r -> r.ignoredFilesHolder.startRescan() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// 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 git4idea.ignore
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
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 git4idea.commands.Git
|
||||
import git4idea.repo.GitRepository
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
import kotlin.concurrent.read
|
||||
import kotlin.concurrent.write
|
||||
|
||||
class GitRepositoryIgnoredHolder(private val project: Project, private val repository: GitRepository, private val git: Git) : Disposable {
|
||||
private val updateQueue = MergingUpdateQueue("GitIgnoreUpdate", 500, true, null, this, null, Alarm.ThreadToUse.POOLED_THREAD)
|
||||
private val inUpdateMode = AtomicBoolean(false)
|
||||
private val ignoredSet = hashSetOf<VirtualFile>()
|
||||
private val SET_LOCK = ReentrantReadWriteLock()
|
||||
private val listeners = EventDispatcher.create(GitRepositoryIgnoredHolderUpdateListener::class.java)
|
||||
|
||||
@JvmOverloads
|
||||
fun startRescan(gitIgnorePath: String? = null) {
|
||||
if (scanTurnedOff()) return
|
||||
|
||||
updateQueue.queue(object : Update("gitRescanIgnored") {
|
||||
override fun canEat(update: Update) = true
|
||||
|
||||
override fun run() {
|
||||
if (inUpdateMode.compareAndSet(false, true)) {
|
||||
fireUpdateStarted(gitIgnorePath)
|
||||
rescanAllIgnored()
|
||||
inUpdateMode.set(false)
|
||||
fireUpdateFinished(gitIgnorePath)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun scanTurnedOff() = !Registry.`is`("git.process.ignored")
|
||||
|
||||
private fun fireUpdateStarted(gitIgnorePath: String? = null) {
|
||||
when(gitIgnorePath){
|
||||
null -> listeners.multicaster.updateStarted()
|
||||
else -> listeners.multicaster.updateStarted(gitIgnorePath)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fireUpdateFinished(gitIgnorePath: String? = null) {
|
||||
when(gitIgnorePath){
|
||||
null -> listeners.multicaster.updateFinished()
|
||||
else -> listeners.multicaster.updateFinished(gitIgnorePath)
|
||||
}
|
||||
}
|
||||
|
||||
fun addUpdateStateListener(listener: GitRepositoryIgnoredHolderUpdateListener) {
|
||||
listeners.addListener(listener, this)
|
||||
}
|
||||
|
||||
fun addFiles(files: List<VirtualFile>) = SET_LOCK.write { ignoredSet.addAll(files) }
|
||||
|
||||
fun addFile(file: VirtualFile) = SET_LOCK.write { ignoredSet.add(file) }
|
||||
|
||||
fun isInUpdateMode() = inUpdateMode.get()
|
||||
|
||||
fun getIgnoredFiles(): Set<VirtualFile> = SET_LOCK.read { ignoredSet.toHashSet() }
|
||||
|
||||
fun containsFile(file: VirtualFile) = SET_LOCK.read { ignoredSet.any { ignoredFile -> VfsUtil.isAncestor(ignoredFile, file, false) } }
|
||||
|
||||
fun getSize() = SET_LOCK.read { ignoredSet.size }
|
||||
|
||||
override fun dispose() {
|
||||
updateQueue.cancelAllUpdates()
|
||||
SET_LOCK.write {
|
||||
ignoredSet.clear()
|
||||
}
|
||||
}
|
||||
|
||||
private fun rescanAllIgnored() {
|
||||
val ignored: Collection<VirtualFile> = HashSet(git.ignoredFiles(project, repository.root))
|
||||
SET_LOCK.write {
|
||||
ignoredSet.clear()
|
||||
ignoredSet.addAll(ignored)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface GitRepositoryIgnoredHolderUpdateListener : EventListener {
|
||||
fun updateStarted()
|
||||
fun updateStarted(gitIgnorePath: String)
|
||||
|
||||
fun updateFinished(gitIgnorePath: String)
|
||||
fun updateFinished()
|
||||
}
|
||||
|
||||
abstract class GitRepositoryIgnoredHolderUpdateAdapter : GitRepositoryIgnoredHolderUpdateListener{
|
||||
override fun updateStarted() {}
|
||||
|
||||
override fun updateStarted(gitIgnorePath: String) {}
|
||||
|
||||
override fun updateFinished(gitIgnorePath: String) {}
|
||||
|
||||
override fun updateFinished() {}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.intellij.util.messages.Topic;
|
||||
import git4idea.GitLocalBranch;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.branch.GitBranchesCollection;
|
||||
import git4idea.ignore.GitRepositoryIgnoredHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -123,4 +124,7 @@ public interface GitRepository extends Repository {
|
||||
*/
|
||||
@NotNull
|
||||
Collection<GitSubmoduleInfo> getSubmodules();
|
||||
|
||||
@NotNull
|
||||
GitRepositoryIgnoredHolder getIgnoredFilesHolder();
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ 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.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.vcs.log.util.StopWatch;
|
||||
@@ -13,6 +17,9 @@ import git4idea.GitLocalBranch;
|
||||
import git4idea.GitUtil;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.branch.GitBranchesCollection;
|
||||
import git4idea.commands.Git;
|
||||
import git4idea.ignore.GitRepositoryIgnoredHolder;
|
||||
import git4idea.ignore.GitRepositoryIgnoredHolderUpdateAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -33,6 +40,7 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
|
||||
@NotNull private final GitRepositoryFiles myRepositoryFiles;
|
||||
|
||||
@Nullable private final GitUntrackedFilesHolder myUntrackedFilesHolder;
|
||||
@Nullable private final GitRepositoryIgnoredHolder myIgnoredRepositoryFilesHolder;
|
||||
|
||||
@NotNull private volatile GitRepoInfo myInfo;
|
||||
|
||||
@@ -47,12 +55,17 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
|
||||
myRepositoryFiles = GitRepositoryFiles.getInstance(gitDir);
|
||||
myReader = new GitRepositoryReader(myRepositoryFiles);
|
||||
myInfo = readRepoInfo();
|
||||
|
||||
if (!light) {
|
||||
myUntrackedFilesHolder = new GitUntrackedFilesHolder(this, myRepositoryFiles);
|
||||
Disposer.register(this, myUntrackedFilesHolder);
|
||||
myIgnoredRepositoryFilesHolder = new GitRepositoryIgnoredHolder(project, this, Git.getInstance());
|
||||
Disposer.register(this, myIgnoredRepositoryFilesHolder);
|
||||
myIgnoredRepositoryFilesHolder.addUpdateStateListener(new MyRepositoryIgnoredHolderUpdateAdapter(getProject()));
|
||||
}
|
||||
else {
|
||||
myUntrackedFilesHolder = null;
|
||||
myIgnoredRepositoryFilesHolder = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +93,9 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
|
||||
private void setupUpdater() {
|
||||
GitRepositoryUpdater updater = new GitRepositoryUpdater(this, myRepositoryFiles);
|
||||
Disposer.register(this, updater);
|
||||
if (myIgnoredRepositoryFilesHolder != null) {
|
||||
myIgnoredRepositoryFilesHolder.startRescan();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@@ -205,7 +221,8 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
|
||||
Collection<GitRemote> remotes = config.parseRemotes();
|
||||
GitBranchState state = myReader.readState(remotes);
|
||||
boolean isShallow = myReader.hasShallowCommits();
|
||||
Collection<GitBranchTrackInfo> trackInfos = config.parseTrackInfos(state.getLocalBranches().keySet(), state.getRemoteBranches().keySet());
|
||||
Collection<GitBranchTrackInfo> trackInfos =
|
||||
config.parseTrackInfos(state.getLocalBranches().keySet(), state.getRemoteBranches().keySet());
|
||||
GitHooksInfo hooksInfo = myReader.readHooksInfo();
|
||||
Collection<GitSubmoduleInfo> submodules = new GitModulesFileReader().read(getSubmoduleFile());
|
||||
sw.report();
|
||||
@@ -219,7 +236,9 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
|
||||
return new File(VfsUtilCore.virtualToIoFile(getRoot()), ".gitmodules");
|
||||
}
|
||||
|
||||
private static void notifyIfRepoChanged(@NotNull final GitRepository repository, @NotNull GitRepoInfo previousInfo, @NotNull GitRepoInfo info) {
|
||||
private static void notifyIfRepoChanged(@NotNull final GitRepository repository,
|
||||
@NotNull GitRepoInfo previousInfo,
|
||||
@NotNull GitRepoInfo info) {
|
||||
if (!repository.getProject().isDisposed() && !info.equals(previousInfo)) {
|
||||
notifyListenersAsync(repository);
|
||||
}
|
||||
@@ -237,4 +256,49 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository {
|
||||
public String toLogString() {
|
||||
return "GitRepository " + getRoot() + " : " + myInfo;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public GitRepositoryIgnoredHolder getIgnoredFilesHolder() {
|
||||
if (myIgnoredRepositoryFilesHolder == null) throw new UnsupportedOperationException("Unsupported for light Git repository");
|
||||
return myIgnoredRepositoryFilesHolder;
|
||||
}
|
||||
|
||||
private static class MyRepositoryIgnoredHolderUpdateAdapter extends GitRepositoryIgnoredHolderUpdateAdapter {
|
||||
@NotNull private final ChangesViewI myChangesViewI;
|
||||
@NotNull private final VcsDirtyScopeManager myDirtyScopeManager;
|
||||
|
||||
MyRepositoryIgnoredHolderUpdateAdapter(@NotNull Project project) {
|
||||
myChangesViewI = ChangesViewManager.getInstance(project);
|
||||
myDirtyScopeManager = VcsDirtyScopeManager.getInstance(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStarted(@NotNull String gitIgnorePath) {
|
||||
myChangesViewI.scheduleRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStarted() {
|
||||
myChangesViewI.scheduleRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFinished(@NotNull String gitIgnorePath) {
|
||||
markGitignoreContainingFolderAsDirty(gitIgnorePath);
|
||||
myChangesViewI.scheduleRefresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFinished() {
|
||||
myChangesViewI.scheduleRefresh();
|
||||
}
|
||||
|
||||
private void markGitignoreContainingFolderAsDirty(@NotNull String gitIgnorePath) {
|
||||
VirtualFile gitIgnore = LocalFileSystem.getInstance().findFileByPath(gitIgnorePath);
|
||||
if (gitIgnore != null && gitIgnore.getParent() != null) {
|
||||
myDirtyScopeManager.dirDirtyRecursively(gitIgnore.getParent());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,7 +291,17 @@ public class GitUntrackedFilesHolder implements Disposable, AsyncVfsEventsListen
|
||||
|
||||
private boolean gitignoreChanged(@NotNull String path) {
|
||||
// TODO watch file stored in core.excludesfile
|
||||
return path.endsWith(".gitignore") || myRepositoryFiles.isExclude(path);
|
||||
boolean gitIgnoreChanged = path.endsWith(GitRepositoryFiles.GITIGNORE);
|
||||
boolean excludeChanged = myRepositoryFiles.isExclude(path);
|
||||
|
||||
if(gitIgnoreChanged || excludeChanged){
|
||||
rescanIgnoredFiles(path);
|
||||
}
|
||||
return gitIgnoreChanged || excludeChanged;
|
||||
}
|
||||
|
||||
private void rescanIgnoredFiles(@NotNull String gitIgnorePath) { //TODO move to ignore manager
|
||||
myRepository.getIgnoredFilesHolder().startRescan(gitIgnorePath);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
@@ -16,6 +17,7 @@ import com.intellij.openapi.vcs.update.RefreshVFsSynchronously;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileEvent;
|
||||
import com.intellij.ui.AppUIUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.vcsUtil.VcsFileUtil;
|
||||
import com.intellij.vcsUtil.VcsUtil;
|
||||
@@ -25,6 +27,7 @@ import git4idea.commands.Git;
|
||||
import git4idea.commands.GitCommand;
|
||||
import git4idea.commands.GitLineHandler;
|
||||
import git4idea.i18n.GitBundle;
|
||||
import git4idea.ignore.GitRepositoryIgnoredHolder;
|
||||
import git4idea.util.GitFileUtils;
|
||||
import git4idea.util.GitVcsConsoleWriter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -109,7 +112,12 @@ public class GitVFSListener extends VcsVFSListener {
|
||||
List<VirtualFile> files = e.getValue();
|
||||
pi.setText(root.getPresentableUrl());
|
||||
try {
|
||||
retainedFiles.addAll(myGit.untrackedFiles(myProject, root, files));
|
||||
Set<VirtualFile> untrackedForRepo = myGit.untrackedFiles(myProject, root, files);
|
||||
if (Registry.is("git.process.ignored")) {
|
||||
List<VirtualFile> ignoredForRepo = ContainerUtil.filter(files, file -> !untrackedForRepo.contains(file));
|
||||
getIgnoreRepoHolder(root).addFiles(ignoredForRepo);
|
||||
}
|
||||
retainedFiles.addAll(untrackedForRepo);
|
||||
}
|
||||
catch (VcsException ex) {
|
||||
myVcsConsoleWriter.showMessage(ex.getMessage());
|
||||
@@ -122,6 +130,11 @@ public class GitVFSListener extends VcsVFSListener {
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private GitRepositoryIgnoredHolder getIgnoreRepoHolder(@NotNull VirtualFile repoRoot) {
|
||||
return ObjectUtils.assertNotNull(GitUtil.getRepositoryManager(myProject).getRepositoryForRootQuick(repoRoot)).getIgnoredFilesHolder();
|
||||
}
|
||||
|
||||
/**
|
||||
* The version of execute add before overriding
|
||||
*
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import git4idea.GitLocalBranch;
|
||||
import git4idea.GitVcs;
|
||||
import git4idea.branch.GitBranchesCollection;
|
||||
import git4idea.ignore.GitRepositoryIgnoredHolder;
|
||||
import git4idea.repo.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -164,6 +165,12 @@ public class MockGitRepository implements GitRepository {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public GitRepositoryIgnoredHolder getIgnoredFilesHolder() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user