vcs-ignore: implement ignored files highlighting for Git (IDEA-116425)

This commit is contained in:
Dmitry Zhuravlev
2018-12-17 14:33:56 +03:00
parent 0fdec399d5
commit 4263251c36
15 changed files with 361 additions and 63 deletions
@@ -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);
}
}
}
}
@@ -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);
}
}
@@ -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() {
}