mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
vcs-ignore: change gitignore generation
* generate ignore file in .idea directory silently * silently generate gitignore after GitHub share action but before initial commit take place * propose generate ignore file after git init action from IDE * new setting and notification for manage ignore files * fix IDEA-201596
This commit is contained in:
@@ -380,8 +380,10 @@ ignored.edit.multiple.files=Selected {0} files
|
||||
ignored.edit.radio.file=Ignore specified &file
|
||||
ignored.edit.radio.directory=Ignore all files &under
|
||||
ignored.edit.radio.mask=Ignore all files &matching
|
||||
ignored.file.generation.message={0} file created
|
||||
ignored.file.generation.review=Open
|
||||
ignored.file.manage.message=Manage VCS ignore files automatically
|
||||
ignored.file.manage.this.project=For this project
|
||||
ignored.file.manage.all.project=For all projects
|
||||
ignored.file.manage.notnow=Not now
|
||||
browse.changes.content.title=Changes under {0}
|
||||
browse.changes.no.filter.prompt=You have not specified any filtering criteria. Are you sure you would like to view the entire history of the project?
|
||||
browse.changes.title=Browse Changes
|
||||
|
||||
@@ -896,7 +896,7 @@ cvs.roots.refresh.uses.vfs.description=Should CVS roots refresh after update use
|
||||
cvs.server.output.max.lines=1000
|
||||
cvs.server.output.max.lines.description=The maximum number of lines that are displayed per command in the CVS console
|
||||
vcs.add.remove.silent=true
|
||||
vcs.ignorefile.generation=false
|
||||
vcs.ignorefile.generation=true
|
||||
vcs.ignorefile.generation.description=If true, native VCS ignored files (e.g. .gitignore) are generated on first project open (import) and after creating a VCS repository from IDEA.
|
||||
|
||||
ide.ui.scale.override=false
|
||||
|
||||
@@ -22,6 +22,7 @@ public class VcsApplicationSettings implements PersistentStateComponent<VcsAppli
|
||||
public boolean SHOW_LST_WORD_DIFFERENCES = true;
|
||||
public boolean DETECT_PATCH_ON_THE_FLY = false;
|
||||
public boolean ENABLE_PARTIAL_CHANGELISTS = true;
|
||||
public boolean MANAGE_IGNORE_FILES = false;
|
||||
|
||||
public static VcsApplicationSettings getInstance() {
|
||||
return ServiceManager.getService(VcsApplicationSettings.class);
|
||||
|
||||
@@ -17,6 +17,8 @@ package com.intellij.openapi.vcs.changes;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -24,17 +26,19 @@ public class IgnoredBeanFactory {
|
||||
private IgnoredBeanFactory() {
|
||||
}
|
||||
|
||||
public static IgnoredFileBean ignoreUnderDirectory(final @NonNls String path, Project p) {
|
||||
final String correctedPath = (path.endsWith("/") || path.endsWith(File.separator)) ? path : path + "/";
|
||||
@NotNull
|
||||
public static IgnoredFileBean ignoreUnderDirectory(@NotNull @NonNls String path, @Nullable Project p) {
|
||||
String correctedPath = (path.endsWith("/") || path.endsWith(File.separator)) ? path : path + "/";
|
||||
return new IgnoredFileBean(correctedPath, IgnoreSettingsType.UNDER_DIR, p);
|
||||
}
|
||||
|
||||
public static IgnoredFileBean ignoreFile(final @NonNls String path, Project p) {
|
||||
// todo check??
|
||||
@NotNull
|
||||
public static IgnoredFileBean ignoreFile(@NotNull @NonNls String path, @Nullable Project p) {
|
||||
return new IgnoredFileBean(path, IgnoreSettingsType.FILE, p);
|
||||
}
|
||||
|
||||
public static IgnoredFileBean withMask(final String mask) {
|
||||
@NotNull
|
||||
public static IgnoredFileBean withMask(@NotNull String mask) {
|
||||
return new IgnoredFileBean(mask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,20 +26,22 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.impl.NullVirtualFile;
|
||||
import com.intellij.util.PathUtilRt;
|
||||
import com.intellij.util.PatternUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class IgnoredFileBean {
|
||||
public class IgnoredFileBean implements IgnoredFileDescriptor {
|
||||
private final String myPath;
|
||||
private final String myFilenameIfFile;
|
||||
private final String myMask;
|
||||
@Nullable private final Pattern myPattern;
|
||||
private final Pattern myPattern;
|
||||
private final IgnoreSettingsType myType;
|
||||
private final Project myProject;
|
||||
private volatile VirtualFile myCachedResolved;
|
||||
|
||||
IgnoredFileBean(String path, IgnoreSettingsType type, Project project) {
|
||||
IgnoredFileBean(@NotNull String path, @NotNull IgnoreSettingsType type, @Nullable Project project) {
|
||||
myPath = path;
|
||||
myType = type;
|
||||
myFilenameIfFile = IgnoreSettingsType.FILE.equals(type) ? PathUtilRt.getFileName(path) : null;
|
||||
@@ -48,42 +50,47 @@ public class IgnoredFileBean {
|
||||
myPattern = null;
|
||||
}
|
||||
|
||||
Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
IgnoredFileBean(String mask) {
|
||||
IgnoredFileBean(@NotNull String mask) {
|
||||
myType = IgnoreSettingsType.MASK;
|
||||
myMask = mask;
|
||||
myPattern = mask != null ? PatternUtil.fromMask(mask) : null;
|
||||
myPattern = PatternUtil.fromMask(mask);
|
||||
myPath = null;
|
||||
myFilenameIfFile = null;
|
||||
myProject = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getPath() {
|
||||
return myPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getMask() {
|
||||
return myMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public IgnoreSettingsType getType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
IgnoredFileBean that = (IgnoredFileBean)o;
|
||||
|
||||
if (myPath != null ? !myPath.equals(that.myPath) : that.myPath != null) return false;
|
||||
if (myMask != null ? !myMask.equals(that.myMask) : that.myMask != null) return false;
|
||||
if (!Objects.equals(myPath, that.myPath)) return false;
|
||||
if (!Objects.equals(myMask, that.myMask)) return false;
|
||||
if (myType != that.myType) return false;
|
||||
|
||||
return true;
|
||||
@@ -97,9 +104,10 @@ public class IgnoredFileBean {
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean matchesFile(VirtualFile file) {
|
||||
@Override
|
||||
public boolean matchesFile(@NotNull VirtualFile file) {
|
||||
if (myType == IgnoreSettingsType.MASK) {
|
||||
return myPattern != null && myPattern.matcher(file.getName()).matches();
|
||||
return myPattern.matcher(file.getName()).matches();
|
||||
}
|
||||
else {
|
||||
// quick check for 'file' == exact match pattern
|
||||
@@ -121,6 +129,7 @@ public class IgnoredFileBean {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private VirtualFile resolve() {
|
||||
if (myCachedResolved == null) {
|
||||
VirtualFile resolved = doResolve();
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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.openapi.vcs.changes;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface IgnoredFileDescriptor {
|
||||
/**
|
||||
* Path to file or directory in case if {@link IgnoreSettingsType#FILE} or {@link IgnoreSettingsType#UNDER_DIR}
|
||||
* @return full path to file or directory. null in case if {@link IgnoreSettingsType} is {@link IgnoreSettingsType#MASK}
|
||||
*/
|
||||
@Nullable
|
||||
String getPath();
|
||||
|
||||
/**
|
||||
* Ignored mask represent ignore pattern in ignore files for different VCS (e.g. .gitignore, .hgignore, etc).
|
||||
* The recommend way to support all VCS is to choose common pattern for mask.
|
||||
* Note: At the moment this mask will be written to ignore file without any pre-processing (as it is)
|
||||
*
|
||||
* @return ignored mask. null in case if {@link IgnoreSettingsType} is not {@link IgnoreSettingsType#MASK}
|
||||
*/
|
||||
@Nullable
|
||||
String getMask();
|
||||
|
||||
@NotNull
|
||||
IgnoreSettingsType getType();
|
||||
|
||||
boolean matchesFile(@NotNull VirtualFile file);
|
||||
}
|
||||
@@ -15,4 +15,6 @@ import java.io.IOException;
|
||||
public interface IgnoredFileGenerator {
|
||||
|
||||
boolean generateFile(@NotNull VirtualFile ignoreFileRoot, @NotNull AbstractVcs vcs) throws IOException;
|
||||
|
||||
boolean appendFile(@NotNull VirtualFile ignoreFileRoot, @NotNull AbstractVcs vcs) throws IOException;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.intellij.openapi.vcs.changes;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -33,8 +32,8 @@ public interface IgnoredFileProvider {
|
||||
boolean isIgnoredFile(@NotNull Project project, @NotNull FilePath filePath);
|
||||
|
||||
@NotNull
|
||||
Set<String> getIgnoredFilesMasks(@NotNull Project project, @NotNull VirtualFile ignoreFileRoot);
|
||||
Set<IgnoredFileDescriptor> getIgnoredFiles(@NotNull Project project);
|
||||
|
||||
@NotNull
|
||||
String getMasksGroupDescription();
|
||||
String getIgnoredGroupDescription();
|
||||
}
|
||||
@@ -1548,41 +1548,23 @@ public class ChangeListManagerImpl extends ChangeListManagerEx implements Projec
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getIgnoredFilesMasks(@NotNull Project project, @NotNull VirtualFile ignoreFileRoot) {
|
||||
Set<String> masks = ContainerUtil.newLinkedHashSet();
|
||||
@SystemIndependent String projectBasePath = project.getBasePath();
|
||||
if (projectBasePath == null) return masks;
|
||||
if (!FileUtil.isAncestor(ignoreFileRoot.getPath(), projectBasePath, false)) return masks;
|
||||
public Set<IgnoredFileDescriptor> getIgnoredFiles(@NotNull Project project) {
|
||||
Set<IgnoredFileBean> ignored = ContainerUtil.newLinkedHashSet();
|
||||
|
||||
String shelfPath = ShelveChangesManager.getShelfPath(project);
|
||||
if (FileUtil.isAncestor(ignoreFileRoot.getPath(), shelfPath, false)) {
|
||||
String relativeShelfPath =
|
||||
FileUtil.getRelativePath(ignoreFileRoot.getPath(), shelfPath, '/');
|
||||
if (relativeShelfPath != null) {
|
||||
masks.add("/" + relativeShelfPath + "/");
|
||||
}
|
||||
ignored.add(IgnoredBeanFactory.ignoreUnderDirectory(shelfPath, project));
|
||||
|
||||
String workspaceFilePath = ProjectKt.getStateStore(project).getWorkspaceFilePath();
|
||||
if (workspaceFilePath != null) {
|
||||
ignored.add(IgnoredBeanFactory.ignoreFile(workspaceFilePath, project));
|
||||
}
|
||||
|
||||
if (ProjectKt.isDirectoryBased(project)) {
|
||||
String workspaceFilePath = ProjectKt.getStateStore(project).getWorkspaceFilePath();
|
||||
if (workspaceFilePath != null && FileUtil.isAncestor(ignoreFileRoot.getPath(), workspaceFilePath, false)) {
|
||||
String relativeWorkspaceFilePath =
|
||||
FileUtil.getRelativePath(ignoreFileRoot.getPath(), workspaceFilePath, '/');
|
||||
if (relativeWorkspaceFilePath != null) {
|
||||
masks.add("/" + relativeWorkspaceFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
masks.add("*." + WorkspaceFileType.DEFAULT_EXTENSION);
|
||||
}
|
||||
|
||||
return ContainerUtil.unmodifiableOrEmptySet(masks);
|
||||
return ContainerUtil.unmodifiableOrEmptySet(ignored);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getMasksGroupDescription() {
|
||||
public String getIgnoredGroupDescription() {
|
||||
return "Default ignored files";
|
||||
}
|
||||
}
|
||||
|
||||
+22
-31
@@ -1,18 +1,14 @@
|
||||
// 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.openapi.vcs.changes;
|
||||
|
||||
import com.intellij.notification.NotificationAction;
|
||||
import com.intellij.openapi.components.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.AbstractVcs;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
import com.intellij.openapi.vcs.VcsNotifier;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
|
||||
@@ -40,6 +36,16 @@ public class IgnoredFileGeneratorImpl implements IgnoredFileGenerator {
|
||||
|
||||
@Override
|
||||
public boolean generateFile(@NotNull VirtualFile ignoreFileRoot, @NotNull AbstractVcs vcs) throws IOException {
|
||||
return doGenerateOrAppend(ignoreFileRoot, vcs, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean appendFile(@NotNull VirtualFile ignoreFileRoot, @NotNull AbstractVcs vcs) throws IOException {
|
||||
return doGenerateOrAppend(ignoreFileRoot, vcs, true);
|
||||
}
|
||||
|
||||
private boolean doGenerateOrAppend(@NotNull VirtualFile ignoreFileRoot,
|
||||
@NotNull AbstractVcs vcs, boolean append) throws IOException {
|
||||
IgnoredFileContentProvider ignoredFileContentProvider = findIgnoredFileContentProvider(vcs);
|
||||
if (ignoredFileContentProvider == null) {
|
||||
LOG.debug("Cannot find content provider for vcs " + vcs.getName());
|
||||
@@ -54,16 +60,18 @@ public class IgnoredFileGeneratorImpl implements IgnoredFileGenerator {
|
||||
}
|
||||
|
||||
synchronized (myWriteLock) {
|
||||
String ignoreFileContent =
|
||||
ignoredFileContentProvider.buildIgnoreFileContent(ignoreFileRoot, IgnoredFileProvider.IGNORE_FILE.getExtensions());
|
||||
if (StringUtil.isEmptyOrSpaces(ignoreFileContent)) return false;
|
||||
|
||||
File ignoreFile = getIgnoreFile(ignoreFileRoot, ignoreFileName);
|
||||
if (!ignoreFile.exists()) {
|
||||
String projectCharsetName = EncodingProjectManager.getInstance(myProject).getDefaultCharsetName();
|
||||
String ignoreFileContent = ignoredFileContentProvider.buildIgnoreFileContent(ignoreFileRoot, IgnoredFileProvider.IGNORE_FILE.getExtensions());
|
||||
FileUtil.writeToFile(ignoreFile, ignoreFileContent.getBytes(projectCharsetName));
|
||||
LocalFileSystem.getInstance().refreshIoFiles(Collections.singleton(ignoreFile));
|
||||
notifyAboutIgnoreFileGeneration(ignoreFile);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (ignoreFile.exists() && !append) return false;
|
||||
|
||||
String projectCharsetName = EncodingProjectManager.getInstance(myProject).getDefaultCharsetName();
|
||||
FileUtil.writeToFile(ignoreFile, ignoreFileContent.getBytes(projectCharsetName), append);
|
||||
IgnoredFileRootStore.getInstance(myProject).addRoot(ignoreFile.getParent());
|
||||
LocalFileSystem.getInstance().refreshIoFiles(Collections.singleton(ignoreFile));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,23 +89,6 @@ public class IgnoredFileGeneratorImpl implements IgnoredFileGenerator {
|
||||
return new File(vcsRootFile.getPath(), ignoreFileName);
|
||||
}
|
||||
|
||||
private void notifyAboutIgnoreFileGeneration(@NotNull File ignoreFile) {
|
||||
IgnoredFileRootStore.getInstance(myProject).addRoot(ignoreFile.getParent());
|
||||
VcsNotifier.getInstance(myProject)
|
||||
.notifyMinorInfo("",
|
||||
VcsBundle.message("ignored.file.generation.message", ignoreFile.getName()),
|
||||
NotificationAction.create(VcsBundle.message("ignored.file.generation.review"), (event, notification) -> {
|
||||
notification.expire();
|
||||
VirtualFile ignoreVirtualFile = VfsUtil.findFileByIoFile(ignoreFile, true);
|
||||
if (ignoreVirtualFile != null) {
|
||||
new OpenFileDescriptor(myProject, ignoreVirtualFile).navigate(true);
|
||||
}
|
||||
else {
|
||||
LOG.warn("Cannot find ignore file " + ignoreFile.getName());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private static boolean needGenerateIgnoreFile(@NotNull Project project, @NotNull VirtualFile ignoreFileRoot) {
|
||||
boolean wasGeneratedPreviously = IgnoredFileRootStore.getInstance(project).containsRoot(ignoreFileRoot.getPath());
|
||||
if (wasGeneratedPreviously) {
|
||||
|
||||
+7
-9
@@ -4,30 +4,28 @@ package com.intellij.openapi.vcs.changes
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vcs.FilePath
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManagerImpl.getInstanceImpl
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesComparator
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
|
||||
class ProjectExcludesIgnoredFileProvider : IgnoredFileProvider {
|
||||
|
||||
override fun isIgnoredFile(project: Project, filePath: FilePath) = getInstanceImpl(project).ignoredFilesComponent.isIgnoredFile(filePath)
|
||||
|
||||
override fun getIgnoredFilesMasks(project: Project, ignoreFileRoot: VirtualFile) = getProjectExcludePathsRelativeTo(project,
|
||||
ignoreFileRoot)
|
||||
override fun getIgnoredFiles(project: Project) = getProjectExcludePathsRelativeTo(project)
|
||||
|
||||
override fun getMasksGroupDescription() = "Project exclude paths"
|
||||
override fun getIgnoredGroupDescription() = "Project exclude paths"
|
||||
|
||||
private fun getProjectExcludePathsRelativeTo(project: Project, ignoreFileRoot: VirtualFile): Set<String> {
|
||||
private fun getProjectExcludePathsRelativeTo(project: Project): Set<IgnoredFileDescriptor> {
|
||||
val excludes = sortedSetOf(ChangesComparator.getVirtualFileComparator(false))
|
||||
|
||||
for (module in ModuleManager.getInstance(project).modules) {
|
||||
if(module.isDisposed) continue
|
||||
|
||||
val roots = ModuleRootManager.getInstance(module).excludeRoots
|
||||
excludes.addAll(roots.filter { root -> VfsUtilCore.isAncestor(ignoreFileRoot, root, false) })
|
||||
excludes.addAll(roots)
|
||||
}
|
||||
|
||||
return excludes.map { root -> "/" + FileUtil.getRelativePath(ignoreFileRoot.path, root.path, '/')!! + '/' }.toSet()
|
||||
return excludes.map { root -> IgnoredBeanFactory.ignoreUnderDirectory(root.path, project) }.toSet()
|
||||
}
|
||||
}
|
||||
@@ -3,30 +3,40 @@ package com.intellij.openapi.vcs.changes
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.ProjectComponent
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vcs.ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED
|
||||
import com.intellij.openapi.vcs.VcsListener
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.project.isDirectoryBased
|
||||
import com.intellij.project.stateStore
|
||||
import com.intellij.vcsUtil.VcsImplUtil
|
||||
import com.intellij.vcsUtil.VcsUtil
|
||||
|
||||
private val LOG = Logger.getInstance(VcsIgnoreFilesChecker::class.java)
|
||||
|
||||
class VcsIgnoreFilesChecker(private val project: Project) : ProjectComponent {
|
||||
|
||||
override fun projectOpened() =
|
||||
project.messageBus
|
||||
.connect()
|
||||
.subscribe(VCS_CONFIGURATION_CHANGED, VcsListener {
|
||||
generateVcsIgnoreFileIfNeeded(project)
|
||||
})
|
||||
override fun projectOpened() {
|
||||
if (project.isDirectoryBased && !ApplicationManager.getApplication().isUnitTestMode) {
|
||||
project.messageBus
|
||||
.connect()
|
||||
.subscribe(VCS_CONFIGURATION_CHANGED, VcsListener {
|
||||
generateVcsIgnoreFileIfNeeded(project)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateVcsIgnoreFileIfNeeded(project: Project) =
|
||||
ApplicationManager.getApplication().executeOnPooledThread {
|
||||
if (!project.isDisposed) {
|
||||
val projectFile = project.projectFile ?: return@executeOnPooledThread
|
||||
if (project.isDisposed) return@executeOnPooledThread
|
||||
|
||||
val projectVcsRoot = VcsUtil.getVcsRootFor(project, projectFile)
|
||||
if (projectVcsRoot != null) {
|
||||
VcsImplUtil.generateIgnoreFileIfNeeded(project, projectVcsRoot)
|
||||
}
|
||||
}
|
||||
val projectConfigDirPath = project.stateStore.projectConfigDir ?: return@executeOnPooledThread
|
||||
val projectConfigDirVFile = LocalFileSystem.getInstance().findFileByPath(projectConfigDirPath) ?: return@executeOnPooledThread
|
||||
|
||||
val vcs = VcsUtil.getVcsFor(project, projectConfigDirVFile) ?: return@executeOnPooledThread
|
||||
|
||||
LOG.debug("Generate VCS ignore file for " + vcs.name)
|
||||
VcsImplUtil.generateIgnoreFileIfNeeded(project, vcs, projectConfigDirVFile)
|
||||
}
|
||||
}
|
||||
+10
-2
@@ -148,7 +148,7 @@
|
||||
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<grid id="aa6eb" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="aa6eb" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
@@ -166,7 +166,7 @@
|
||||
</component>
|
||||
<grid id="6c335" layout-manager="FlowLayout" hgap="5" vgap="2" flow-align="0">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -198,6 +198,14 @@
|
||||
<text value="Restore workspace on &branch switching"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="2334f" class="javax.swing.JCheckBox" binding="myManageIgnoreFiles">
|
||||
<constraints>
|
||||
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text value="Manage &ignore files"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
|
||||
+7
@@ -62,6 +62,7 @@ public class VcsGeneralConfigurationPanel {
|
||||
private JCheckBox myReloadContext;
|
||||
private JLabel myOnPatchCreationLabel;
|
||||
private JPanel myEmptyChangeListPanel;
|
||||
private JCheckBox myManageIgnoreFiles;
|
||||
private ButtonGroup myEmptyChangelistRemovingGroup;
|
||||
|
||||
public VcsGeneralConfigurationPanel(final Project project) {
|
||||
@@ -102,9 +103,11 @@ public class VcsGeneralConfigurationPanel {
|
||||
public void apply() {
|
||||
|
||||
VcsConfiguration settings = VcsConfiguration.getInstance(myProject);
|
||||
VcsApplicationSettings applicationSettings = VcsApplicationSettings.getInstance();
|
||||
|
||||
settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS = getSelected(myEmptyChangelistRemovingGroup);
|
||||
settings.RELOAD_CONTEXT = myReloadContext.isSelected();
|
||||
applicationSettings.MANAGE_IGNORE_FILES = myManageIgnoreFiles.isSelected();
|
||||
|
||||
for (VcsShowOptionsSettingImpl setting : myPromptOptions.keySet()) {
|
||||
setting.setValue(myPromptOptions.get(setting).isSelected());
|
||||
@@ -186,10 +189,12 @@ public class VcsGeneralConfigurationPanel {
|
||||
public boolean isModified() {
|
||||
|
||||
VcsConfiguration settings = VcsConfiguration.getInstance(myProject);
|
||||
VcsApplicationSettings applicationSettings = VcsApplicationSettings.getInstance();
|
||||
if (settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS != getSelected(myEmptyChangelistRemovingGroup)){
|
||||
return true;
|
||||
}
|
||||
if (settings.RELOAD_CONTEXT != myReloadContext.isSelected()) return true;
|
||||
if (applicationSettings.MANAGE_IGNORE_FILES != myManageIgnoreFiles.isSelected()) return true;
|
||||
|
||||
if (getReadOnlyStatusHandler().getState().SHOW_DIALOG != myShowReadOnlyStatusDialog.isSelected()) {
|
||||
return true;
|
||||
@@ -208,7 +213,9 @@ public class VcsGeneralConfigurationPanel {
|
||||
|
||||
public void reset() {
|
||||
VcsConfiguration settings = VcsConfiguration.getInstance(myProject);
|
||||
VcsApplicationSettings applicationSettings = VcsApplicationSettings.getInstance();
|
||||
myReloadContext.setSelected(settings.RELOAD_CONTEXT);
|
||||
myManageIgnoreFiles.setSelected(applicationSettings.MANAGE_IGNORE_FILES);
|
||||
VcsShowConfirmationOption.Value value = settings.REMOVE_EMPTY_INACTIVE_CHANGELISTS;
|
||||
UIUtil.setSelectedButton(myEmptyChangelistRemovingGroup, value == VcsShowConfirmationOption.Value.SHOW_CONFIRMATION
|
||||
? 0
|
||||
|
||||
@@ -54,7 +54,7 @@ public abstract class VcsIntegrationEnabler {
|
||||
if (roots.isEmpty()) {
|
||||
boolean succeeded = initOrNotifyError(projectDir);
|
||||
if (succeeded) {
|
||||
VcsImplUtil.generateIgnoreFileIfNeeded(myProject, myVcs, projectDir);
|
||||
VcsImplUtil.proposeUpdateIgnoreFile(myProject, myVcs, projectDir);
|
||||
addVcsRoots(Collections.singleton(projectDir));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
// 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.vcsUtil;
|
||||
|
||||
import com.intellij.ide.util.PropertiesComponent;
|
||||
import com.intellij.notification.NotificationAction;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vcs.AbstractVcs;
|
||||
import com.intellij.openapi.vcs.VcsApplicationSettings;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
import com.intellij.openapi.vcs.VcsNotifier;
|
||||
import com.intellij.openapi.vcs.changes.IgnoredFileContentProvider;
|
||||
import com.intellij.openapi.vcs.changes.IgnoredFileGenerator;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.WaitForProgressToShow;
|
||||
@@ -15,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* <p>{@link VcsUtil} extension that needs access to the {@code intellij.platform.vcs.impl} module.</p>
|
||||
@@ -23,6 +32,8 @@ public class VcsImplUtil {
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(VcsImplUtil.class);
|
||||
|
||||
public static final String MANAGE_IGNORE_FILES_PROPERTY = "MANAGE_IGNORE_FILES_PROPERTY";
|
||||
|
||||
/**
|
||||
* Shows error message with specified message text and title.
|
||||
* The parent component is the root frame.
|
||||
@@ -55,31 +66,95 @@ public class VcsImplUtil {
|
||||
return Registry.is("vcs.non.modal.commit");
|
||||
}
|
||||
|
||||
public static void generateIgnoreFileIfNeeded(@NotNull Project project, @NotNull VirtualFile vcsRoot) {
|
||||
AbstractVcs vcs = VcsUtil.getVcsFor(project, vcsRoot);
|
||||
if (vcs == null) {
|
||||
LOG.debug("Cannot get VCS for root " + vcsRoot.getPath());
|
||||
return;
|
||||
}
|
||||
public static void proposeUpdateIgnoreFile(@NotNull Project project,
|
||||
@NotNull AbstractVcs vcs,
|
||||
@NotNull VirtualFile ignoreFileRoot) {
|
||||
IgnoredFileContentProvider ignoreContentProvider = getIgnoredFileContentProvider(project, vcs);
|
||||
|
||||
LOG.debug("Generate VCS ignore file for " + vcs.getName());
|
||||
generateIgnoreFileIfNeeded(project, vcs, vcsRoot);
|
||||
if (ignoreContentProvider == null) {
|
||||
LOG.debug("Cannot get ignore content provider for vcs " + vcs.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
String ignoreFileName = ignoreContentProvider.getFileName();
|
||||
File ignoreFile = Paths.get(ignoreFileRoot.getPath(), ignoreFileName).toFile();
|
||||
|
||||
if (canManageIgnoreFiles(project)) {
|
||||
updateIgnoreFileIfNeeded(project, vcs, ignoreFileRoot, ignoreFile.exists());
|
||||
}
|
||||
else {
|
||||
notifyVcsIgnoreFileManage(project, () -> updateIgnoreFileAndOpen(project, vcs, ignoreFileRoot, ignoreFile));
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyVcsIgnoreFileManage(@NotNull Project project,
|
||||
@NotNull Runnable manageIgnore) {
|
||||
PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project);
|
||||
VcsApplicationSettings applicationSettings = VcsApplicationSettings.getInstance();
|
||||
|
||||
VcsNotifier.getInstance(project).notifyMinorInfo(
|
||||
"",
|
||||
VcsBundle.message("ignored.file.manage.message"),
|
||||
NotificationAction.create(VcsBundle.message("ignored.file.manage.this.project"), (event, notification) -> {
|
||||
manageIgnore.run();
|
||||
propertiesComponent.setValue(MANAGE_IGNORE_FILES_PROPERTY, true);
|
||||
notification.expire();
|
||||
}),
|
||||
NotificationAction.create(VcsBundle.message("ignored.file.manage.all.project"), (event, notification) -> {
|
||||
manageIgnore.run();
|
||||
applicationSettings.MANAGE_IGNORE_FILES = true;
|
||||
notification.expire();
|
||||
}),
|
||||
NotificationAction.create(VcsBundle.message("ignored.file.manage.notnow"), (event, notification) -> {
|
||||
notification.expire();
|
||||
}));
|
||||
}
|
||||
|
||||
public static boolean generateIgnoreFileIfNeeded(@NotNull Project project,
|
||||
@NotNull AbstractVcs vcs,
|
||||
@NotNull VirtualFile ignoreFileRoot) {
|
||||
return updateIgnoreFileIfNeeded(project, vcs, ignoreFileRoot, false);
|
||||
}
|
||||
|
||||
public static boolean updateIgnoreFileIfNeeded(@NotNull Project project,
|
||||
@NotNull AbstractVcs vcs,
|
||||
@NotNull VirtualFile ignoreFileRoot, boolean append) {
|
||||
IgnoredFileGenerator ignoredFileGenerator = ServiceManager.getService(project, IgnoredFileGenerator.class);
|
||||
if (ignoredFileGenerator == null) {
|
||||
LOG.debug("Cannot find ignore file ignoredFileGenerator for " + vcs.getName() + " VCS");
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return ignoredFileGenerator.generateFile(ignoreFileRoot, vcs);
|
||||
return append ? ignoredFileGenerator.appendFile(ignoreFileRoot, vcs) : ignoredFileGenerator.generateFile(ignoreFileRoot, vcs);
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.warn(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateIgnoreFileAndOpen(@NotNull Project project,
|
||||
@NotNull AbstractVcs vcs,
|
||||
@NotNull VirtualFile ignoreFileRoot, @NotNull File ignoreFile) {
|
||||
if (updateIgnoreFileIfNeeded(project, vcs, ignoreFileRoot, ignoreFile.exists())) {
|
||||
VirtualFile ignoreVFile = VfsUtil.findFileByIoFile(ignoreFile, true);
|
||||
if (ignoreVFile == null) return;
|
||||
new OpenFileDescriptor(project, ignoreVFile).navigate(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static IgnoredFileContentProvider getIgnoredFileContentProvider(@NotNull Project project,
|
||||
@NotNull AbstractVcs vcs) {
|
||||
return IgnoredFileContentProvider.IGNORE_FILE_CONTENT_PROVIDER.extensions(project)
|
||||
.filter((provider) -> provider.getSupportedVcs().equals(vcs.getKeyInstanceMethod()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static boolean canManageIgnoreFiles(@NotNull Project project) {
|
||||
PropertiesComponent propertiesComponent = PropertiesComponent.getInstance(project);
|
||||
VcsApplicationSettings applicationSettings = VcsApplicationSettings.getInstance();
|
||||
|
||||
return applicationSettings.MANAGE_IGNORE_FILES || propertiesComponent.getBoolean(MANAGE_IGNORE_FILES_PROPERTY, false);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -352,7 +352,7 @@ public class CloudGitDeploymentRuntime extends CloudDeploymentRuntime {
|
||||
protected void refreshApplicationRepository() {
|
||||
Project project = getProject();
|
||||
GitInit.refreshAndConfigureVcsMappings(project, getRepositoryRoot(), getRepositoryRootFile().getAbsolutePath());
|
||||
GitUtil.generateGitignoreFileIfNeeded(project, getRepositoryRoot());
|
||||
GitUtil.proposeUpdateGitignore(project, getRepositoryRoot());
|
||||
}
|
||||
|
||||
protected void pushApplication(@NotNull CloudGitApplication application) throws ServerRuntimeException {
|
||||
|
||||
@@ -1052,4 +1052,8 @@ public class GitUtil {
|
||||
public static void generateGitignoreFileIfNeeded(@NotNull Project project, @NotNull VirtualFile ignoreFileRoot) {
|
||||
VcsImplUtil.generateIgnoreFileIfNeeded(project, GitVcs.getInstance(project), ignoreFileRoot);
|
||||
}
|
||||
|
||||
public static void proposeUpdateGitignore(@NotNull Project project, @NotNull VirtualFile ignoreFileRoot) {
|
||||
VcsImplUtil.proposeUpdateIgnoreFile(project, GitVcs.getInstance(project), ignoreFileRoot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class GitInit extends DumbAwareAction {
|
||||
@Override
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
refreshAndConfigureVcsMappings(project, root, root.getPath());
|
||||
GitUtil.generateGitignoreFileIfNeeded(project, root);
|
||||
GitUtil.proposeUpdateGitignore(project, root);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,11 +2,21 @@
|
||||
package git4idea.ignore
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Comparing
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vcs.VcsKey
|
||||
import com.intellij.openapi.vcs.actions.VcsContextFactory
|
||||
import com.intellij.openapi.vcs.changes.IgnoreSettingsType.*
|
||||
import com.intellij.openapi.vcs.changes.IgnoredFileContentProvider
|
||||
import com.intellij.openapi.vcs.changes.IgnoredFileDescriptor
|
||||
import com.intellij.openapi.vcs.changes.IgnoredFileProvider
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.project.stateStore
|
||||
import com.intellij.vcsUtil.VcsUtil
|
||||
import git4idea.GitUtil
|
||||
import git4idea.GitVcs
|
||||
import git4idea.commands.Git
|
||||
import git4idea.repo.GitRepositoryFiles.GITIGNORE
|
||||
import java.lang.System.lineSeparator
|
||||
|
||||
@@ -17,28 +27,83 @@ open class GitIgnoredFileContentProvider(private val project: Project) : Ignored
|
||||
override fun getFileName() = GITIGNORE
|
||||
|
||||
override fun buildIgnoreFileContent(ignoreFileRoot: VirtualFile, ignoredFileProviders: Array<IgnoredFileProvider>): String {
|
||||
if (!GitUtil.isUnderGit(ignoreFileRoot)) return "" //if ignore file root not under git --> return (e.g. in case if .git folder was deleted externally)
|
||||
|
||||
val content = StringBuilder()
|
||||
val lineSeparator = lineSeparator()
|
||||
val untrackedFiles = Git.getInstance().untrackedFiles(project, ignoreFileRoot, null)
|
||||
|
||||
for (i in ignoredFileProviders.indices) {
|
||||
val provider = ignoredFileProviders[i]
|
||||
val ignoredFileMasks = provider.getIgnoredFilesMasks(project, ignoreFileRoot)
|
||||
if (ignoredFileMasks.isEmpty()) continue
|
||||
val ignoredFiles = provider.getIgnoredFiles(project).ignoreBeansToRelativePaths(ignoreFileRoot, untrackedFiles)
|
||||
|
||||
if (ignoredFiles.isEmpty()) continue
|
||||
|
||||
if (!content.isEmpty()) {
|
||||
content.append(lineSeparator).append(lineSeparator)
|
||||
}
|
||||
|
||||
val description = provider.masksGroupDescription
|
||||
val description = provider.ignoredGroupDescription
|
||||
if (description.isNotBlank()) {
|
||||
content.append(prependCommentHashCharacterIfNeeded(description))
|
||||
content.append(lineSeparator)
|
||||
}
|
||||
content.append(ignoredFileMasks.joinToString(lineSeparator))
|
||||
content.append(ignoredFiles.joinToString(lineSeparator))
|
||||
}
|
||||
return content.toString()
|
||||
}
|
||||
|
||||
private fun Iterable<IgnoredFileDescriptor>.ignoreBeansToRelativePaths(ignoreFileRoot: VirtualFile, untrackedFiles: Set<VirtualFile>): List<String> {
|
||||
val vcsRoot= VcsUtil.getVcsRootFor(project, ignoreFileRoot)
|
||||
val vcsContextFactory = VcsContextFactory.SERVICE.getInstance()
|
||||
return filter { ignoredBean ->
|
||||
when (ignoredBean.type) {
|
||||
UNDER_DIR -> shouldIgnoreUnderDir(ignoredBean, ignoreFileRoot, vcsRoot, vcsContextFactory)
|
||||
FILE -> shouldIgnoreFile(ignoredBean, untrackedFiles, ignoreFileRoot, vcsRoot, vcsContextFactory)
|
||||
MASK -> shouldIgnoreByMask(ignoredBean, untrackedFiles)
|
||||
}
|
||||
}.map { ignoredBean ->
|
||||
when (ignoredBean.type) {
|
||||
MASK -> ignoredBean.mask!!
|
||||
UNDER_DIR -> "/${FileUtil.getRelativePath(ignoreFileRoot.path, ignoredBean.path!!, '/')!!}"
|
||||
FILE -> "/${FileUtil.getRelativePath(ignoreFileRoot.path, ignoredBean.path!!, '/')!!}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldIgnoreUnderDir(ignoredBean: IgnoredFileDescriptor,
|
||||
ignoreFileRoot: VirtualFile,
|
||||
vcsRoot: VirtualFile?,
|
||||
vcsContextFactory: VcsContextFactory) =
|
||||
FileUtil.exists(ignoredBean.path)
|
||||
&& FileUtil.isAncestor(ignoreFileRoot.path, ignoredBean.path!!, false)
|
||||
&& Comparing.equal(vcsRoot, VcsUtil.getVcsRootFor(project, vcsContextFactory.createFilePath(ignoredBean.path!!, true)))
|
||||
&& shouldNotConsiderInternalIgnoreFile(ignoredBean, ignoreFileRoot)
|
||||
|
||||
private fun shouldIgnoreFile(ignoredBean: IgnoredFileDescriptor,
|
||||
untrackedFiles: Set<VirtualFile>,
|
||||
ignoreFileRoot: VirtualFile,
|
||||
vcsRoot: VirtualFile?,
|
||||
vcsContextFactory: VcsContextFactory) =
|
||||
FileUtil.exists(ignoredBean.path)
|
||||
&& untrackedFiles.any { ignoredBean.matchesFile(it) }
|
||||
&& FileUtil.isAncestor(ignoreFileRoot.path, ignoredBean.path!!, false)
|
||||
&& Comparing.equal(vcsRoot, VcsUtil.getVcsRootFor(project, vcsContextFactory.createFilePath(ignoredBean.path!!, false)))
|
||||
&& shouldNotConsiderInternalIgnoreFile(ignoredBean, ignoreFileRoot)
|
||||
|
||||
private fun shouldIgnoreByMask(ignoredBean: IgnoredFileDescriptor, untrackedFiles: Set<VirtualFile>) =
|
||||
untrackedFiles.any { ignoredBean.matchesFile(it) }
|
||||
|
||||
private fun shouldNotConsiderInternalIgnoreFile(ignoredBean: IgnoredFileDescriptor, ignoreFileRoot: VirtualFile): Boolean {
|
||||
val insideDirectoryStore = ignoredBean.path?.contains(Project.DIRECTORY_STORE_FOLDER) ?: false
|
||||
if (insideDirectoryStore) {
|
||||
val directoryStoreOrProjectFileLocation = project.stateStore.directoryStoreFile ?: project.projectFile?.parent ?: return false
|
||||
return FileUtil.isAncestor(VfsUtilCore.virtualToIoFile(directoryStoreOrProjectFileLocation),
|
||||
VfsUtilCore.virtualToIoFile(ignoreFileRoot), false)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun prependCommentHashCharacterIfNeeded(description: String): String =
|
||||
if (description.startsWith("#")) description else "# $description"
|
||||
}
|
||||
|
||||
@@ -2,27 +2,42 @@
|
||||
package git4idea.ignore
|
||||
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.project.Project.DIRECTORY_STORE_FOLDER
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.encoding.EncodingProjectManager
|
||||
import com.intellij.project.stateStore
|
||||
import git4idea.GitUtil
|
||||
import git4idea.repo.GitRepositoryFiles.GITIGNORE
|
||||
import git4idea.test.GitPlatformTest
|
||||
import git4idea.test.createRepository
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
const val OUT = "out"
|
||||
const val EXCLUDED = "excluded"
|
||||
const val EXCLUDED_CHILD = "child"
|
||||
const val EXCLUDED_CHILD_DIR = "$EXCLUDED/$EXCLUDED_CHILD"
|
||||
const val EXCLUDED_CHILD_DIR = "child"
|
||||
const val EXCLUDED_CHILD = "$EXCLUDED/$EXCLUDED_CHILD_DIR"
|
||||
const val SHELF = "shelf"
|
||||
|
||||
class GitIgnoredFileTest : GitPlatformTest() {
|
||||
|
||||
override fun getProjectDirOrFile(): Path {
|
||||
val projectRoot = File(testRoot, "project")
|
||||
val file: File = FileUtil.createTempDirectory(projectRoot, FileUtil.sanitizeFileName(name, true), "")
|
||||
val ideaDir = file.resolve(DIRECTORY_STORE_FOLDER)
|
||||
ideaDir.mkdir()
|
||||
return file.toPath()
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
Registry.get("vcs.ignorefile.generation").setValue(true, testRootDisposable)
|
||||
createRepository(project, projectPath)
|
||||
GitUtil.generateGitignoreFileIfNeeded(project, projectRoot)
|
||||
}
|
||||
|
||||
override fun setUpModule() {
|
||||
@@ -32,32 +47,57 @@ class GitIgnoredFileTest : GitPlatformTest() {
|
||||
myModule.addContentRoot(moduleDir)
|
||||
val outDir = moduleDir.findOrCreateDir(OUT)
|
||||
val excludedDir = moduleDir.findOrCreateDir(EXCLUDED)
|
||||
val excludedDirChild = excludedDir.findOrCreateDir(EXCLUDED_CHILD)
|
||||
val excludedChildDir = excludedDir.findOrCreateDir(EXCLUDED_CHILD_DIR)
|
||||
myModule.addExclude(outDir)
|
||||
myModule.addExclude(excludedDir)
|
||||
myModule.addExclude(excludedDirChild)
|
||||
myModule.addExclude(excludedChildDir)
|
||||
}
|
||||
}
|
||||
|
||||
fun `test gitignore created`() {
|
||||
assertTrue(File("$projectPath/$GITIGNORE").exists())
|
||||
fun `test gitignore content in config dir`() {
|
||||
val gitIgnore = File("$projectPath/$DIRECTORY_STORE_FOLDER/$GITIGNORE")
|
||||
if (gitIgnore.exists()) gitIgnore.delete()
|
||||
|
||||
val shelf = File(ShelveChangesManager.getShelfPath(project))
|
||||
val shelfExist = if (shelf.exists()) true else shelf.mkdir()
|
||||
if (!shelfExist) fail("Shelf doesn't exist and cannot be created")
|
||||
|
||||
val workspaceFilePath = project.stateStore.workspaceFilePath
|
||||
if (workspaceFilePath == null) fail("Cannot detect workspace file path")
|
||||
val workspaceFile = File(workspaceFilePath!!)
|
||||
val workspaceFileExist = FileUtil.createIfNotExists(workspaceFile)
|
||||
if (!workspaceFileExist || VfsUtil.findFileByIoFile(workspaceFile, true) == null) fail("Workspace file doesn't exist and cannot be created")
|
||||
|
||||
GitUtil.generateGitignoreFileIfNeeded(project, VfsUtil.findFile(Paths.get("$projectPath/$DIRECTORY_STORE_FOLDER"), true)!!)
|
||||
|
||||
assertGitignoreValid(gitIgnore,
|
||||
"""
|
||||
# Default ignored files
|
||||
/$SHELF/
|
||||
/${workspaceFile.name}
|
||||
""")
|
||||
}
|
||||
|
||||
fun `test gitignore content`() {
|
||||
val projectCharset = EncodingProjectManager.getInstance(project).defaultCharset
|
||||
val gitIgnoreExpectedContentList = """
|
||||
# Default ignored files
|
||||
/.shelf/
|
||||
*.iws
|
||||
fun `test gitignore content in project root`() {
|
||||
GitUtil.generateGitignoreFileIfNeeded(project, projectRoot)
|
||||
|
||||
val gitIgnore = File("$projectPath/$GITIGNORE")
|
||||
|
||||
assertGitignoreValid(gitIgnore,
|
||||
"""
|
||||
# Project exclude paths
|
||||
/$EXCLUDED/
|
||||
/$EXCLUDED_CHILD_DIR/
|
||||
/$EXCLUDED_CHILD/
|
||||
/$OUT/
|
||||
""".trimIndent().lines()
|
||||
val gitIgnoreFile = File("$projectPath/$GITIGNORE")
|
||||
assertTrue(gitIgnoreFile.exists())
|
||||
val generatedGitIgnoreContent = gitIgnoreFile.readText(projectCharset)
|
||||
""")
|
||||
}
|
||||
|
||||
private fun assertGitignoreValid(ignoreFile: File, gitIgnoreExpectedContent: String) {
|
||||
val projectCharset = EncodingProjectManager.getInstance(project).defaultCharset
|
||||
val gitIgnoreExpectedContentList = gitIgnoreExpectedContent.trimIndent().lines()
|
||||
|
||||
assertTrue(ignoreFile.exists())
|
||||
val generatedGitIgnoreContent = ignoreFile.readText(projectCharset)
|
||||
assertFalse("Generated ignore file is empty", generatedGitIgnoreContent.isBlank())
|
||||
assertFalse("Generated ignore file content should be system-independent", generatedGitIgnoreContent.contains('\\'))
|
||||
assertContainsOrdered(generatedGitIgnoreContent.lines(), gitIgnoreExpectedContentList)
|
||||
|
||||
@@ -236,7 +236,6 @@ class GithubShareAction : DumbAwareAction("Share Project on GitHub", "Easily sha
|
||||
return false
|
||||
}
|
||||
GitInit.refreshAndConfigureVcsMappings(project, root, root.path)
|
||||
//even if gitignore file can be created after VCS configuration changed, we call this explicitly just to be sure that gitignore file will be added to initial commit
|
||||
GitUtil.generateGitignoreFileIfNeeded(project, root)
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user