Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/main/java/implementation/compare/ChangesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vcs.changes.ChangesUtil;
import com.intellij.openapi.vcs.changes.CurrentContentRevision;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitCommit;
import git4idea.GitReference;
Expand All @@ -22,6 +24,7 @@
import model.TargetBranchMap;
import org.jetbrains.annotations.NotNull;
import service.GitService;
import settings.GitScopeSettings;
import system.Defs;
import utils.PlatformApiReflection;
import utils.GitUtil;
Expand Down Expand Up @@ -249,26 +252,27 @@ public void clearCache(GitRepository repo) {
*/
private Collection<Change> filterLocalChanges(Collection<Change> localChanges, String repoPath, Collection<Change> existingChanges) {
Collection<Change> filtered = new ArrayList<>();
boolean showDeletedFiles = GitScopeSettings.getInstance().isShowDeletedFiles();

for (Change change : localChanges) {
VirtualFile changeFile = change.getVirtualFile();
if (changeFile == null) {
FilePath changePath = ChangesUtil.getFilePath(change);
String changePathStr = changePath.getPath();

if (!showDeletedFiles && change.getType() == Change.Type.DELETED) {
continue;
}

String changePath = changeFile.getPath();

// Check if change belongs to this repository
if (!changePath.startsWith(repoPath)) {
if (!changePathStr.startsWith(repoPath)) {
continue;
}

// If existingChanges provided, skip duplicates
if (existingChanges != null && !existingChanges.isEmpty()) {
boolean isDuplicate = false;
for (Change existing : existingChanges) {
VirtualFile existingFile = existing.getVirtualFile();
if (existingFile != null && changePath.equals(existingFile.getPath())) {
String existingPath = ChangesUtil.getFilePath(existing).getPath();
if (changePathStr.equals(existingPath)) {
isDuplicate = true;
break;
}
Expand Down Expand Up @@ -334,6 +338,17 @@ public RepoChangesResult doCollectChanges(Project project, GitRepository repo, S
// Filter local changes for this repository
repoLocalChanges = filterLocalChanges(localChanges, repoPath, null);

// Add unversioned (untracked) files if the setting is enabled
if (GitScopeSettings.getInstance().isShowUntrackedFiles()) {
for (FilePath unversionedPath : changeListManager.getUnversionedFilesPaths()) {
String filePathStr = unversionedPath.getPath();
if (filePathStr.startsWith(repoPath)) {
Change untrackedChange = new Change(null, new CurrentContentRevision(unversionedPath), FileStatus.UNKNOWN);
repoLocalChanges.add(untrackedChange);
}
}
}

// Special handling for HEAD - return local changes only, no scope changes
if (scopeRef.equals(GitService.BRANCH_HEAD)) {
return new RepoChangesResult(new ArrayList<>(repoLocalChanges), new ArrayList<>(), repoLocalChanges);
Expand Down
9 changes: 4 additions & 5 deletions src/main/java/model/MyModel.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package model;

import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vcs.changes.ChangesUtil;
import git4idea.repo.GitRepository;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.PublishSubject;
Expand Down Expand Up @@ -172,6 +172,7 @@ public Map<String, Change> getLocalChangesMap() {
/**
* Helper method to build a HashMap from a collection of changes indexed by file path.
* This provides O(1) lookup performance for file status checks.
* Uses ChangesUtil.getFilePath() to properly handle deleted files (where getVirtualFile() is null).
*
* @param changes Collection of changes to convert to a map
* @return Map of file path to Change, or null if changes is null
Expand All @@ -183,10 +184,8 @@ public static Map<String, Change> buildChangesByPathMap(Collection<Change> chang

Map<String, Change> changeMap = new HashMap<>();
for (Change change : changes) {
VirtualFile file = change.getVirtualFile();
if (file != null) {
changeMap.put(file.getPath(), change);
}
String path = ChangesUtil.getFilePath(change).getPath();
changeMap.put(path, change);
}
return changeMap;
}
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/settings/GitScopeSettings.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ public class GitScopeSettings implements PersistentStateComponent<GitScopeSettin
*/
public boolean scopeFileColors = true;

/**
* If true, untracked (unversioned) files are shown in the Git Scope view.
* Default: false
*/
public boolean showUntrackedFiles = false;

/**
* If true, locally deleted files are shown in the Git Scope view.
* Default: false
*/
public boolean showDeletedFiles = false;

public static GitScopeSettings getInstance() {
return ApplicationManager.getApplication().getService(GitScopeSettings.class);
}
Expand Down Expand Up @@ -63,4 +75,20 @@ public boolean isScopeFileColors() {
public void setScopeFileColors(boolean scopeFileColors) {
this.scopeFileColors = scopeFileColors;
}

public boolean isShowUntrackedFiles() {
return showUntrackedFiles;
}

public void setShowUntrackedFiles(boolean showUntrackedFiles) {
this.showUntrackedFiles = showUntrackedFiles;
}

public boolean isShowDeletedFiles() {
return showDeletedFiles;
}

public void setShowDeletedFiles(boolean showDeletedFiles) {
this.showDeletedFiles = showDeletedFiles;
}
}
32 changes: 32 additions & 0 deletions src/main/java/settings/GitScopeSettingsComponent.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class GitScopeSettingsComponent {
private final JPanel mainPanel;
private final JBCheckBox separateGutterRenderingCheckBox;
private final JBCheckBox scopeFileColorsCheckBox;
private final JBCheckBox showUntrackedFilesCheckBox;
private final JBCheckBox showDeletedFilesCheckBox;

public GitScopeSettingsComponent() {
separateGutterRenderingCheckBox = new JBCheckBox(
Expand All @@ -25,6 +27,14 @@ public GitScopeSettingsComponent() {
"Color files based on Git Scope"
);

showUntrackedFilesCheckBox = new JBCheckBox(
"See untracked files"
);

showDeletedFilesCheckBox = new JBCheckBox(
"See deleted files"
);

mainPanel = FormBuilder.createFormBuilder()
.addComponent(new TitledSeparator("Gutter Rendering"))
.addComponent(separateGutterRenderingCheckBox, 1)
Expand All @@ -33,6 +43,12 @@ public GitScopeSettingsComponent() {
.addComponent(new TitledSeparator("File Colors"))
.addComponent(scopeFileColorsCheckBox, 1)
.addTooltip("When enabled (default), project and editor file colors reflect the active Git Scope")
.addVerticalGap(10)
.addComponent(new TitledSeparator("Working Tree"))
.addComponent(showUntrackedFilesCheckBox, 1)
.addTooltip("When enabled (default), untracked (unversioned) files appear in the Git Scope view")
.addComponent(showDeletedFilesCheckBox, 1)
.addTooltip("When enabled (default), locally deleted files appear in the Git Scope view")
.addComponentFillVertically(new JPanel(), 0)
.getPanel();

Expand Down Expand Up @@ -62,4 +78,20 @@ public boolean isScopeFileColors() {
public void setScopeFileColors(boolean value) {
scopeFileColorsCheckBox.setSelected(value);
}

public boolean isShowUntrackedFiles() {
return showUntrackedFilesCheckBox.isSelected();
}

public void setShowUntrackedFiles(boolean value) {
showUntrackedFilesCheckBox.setSelected(value);
}

public boolean isShowDeletedFiles() {
return showDeletedFilesCheckBox.isSelected();
}

public void setShowDeletedFiles(boolean value) {
showDeletedFilesCheckBox.setSelected(value);
}
}
23 changes: 17 additions & 6 deletions src/main/java/settings/GitScopeSettingsConfigurable.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,32 @@ public JComponent createComponent() {
public boolean isModified() {
GitScopeSettings settings = GitScopeSettings.getInstance();
return settingsComponent.isSeparateGutterRendering() != settings.isSeparateGutterRendering()
|| settingsComponent.isScopeFileColors() != settings.isScopeFileColors();
|| settingsComponent.isScopeFileColors() != settings.isScopeFileColors()
|| settingsComponent.isShowUntrackedFiles() != settings.isShowUntrackedFiles()
|| settingsComponent.isShowDeletedFiles() != settings.isShowDeletedFiles();
}

@Override
public void apply() throws ConfigurationException {
GitScopeSettings settings = GitScopeSettings.getInstance();
boolean tabColorsChanged = settingsComponent.isScopeFileColors() != settings.isScopeFileColors();
boolean workingTreeChanged = settingsComponent.isShowUntrackedFiles() != settings.isShowUntrackedFiles()
|| settingsComponent.isShowDeletedFiles() != settings.isShowDeletedFiles();
settings.setSeparateGutterRendering(settingsComponent.isSeparateGutterRendering());
settings.setScopeFileColors(settingsComponent.isScopeFileColors());
settings.setShowUntrackedFiles(settingsComponent.isShowUntrackedFiles());
settings.setShowDeletedFiles(settingsComponent.isShowDeletedFiles());

if (tabColorsChanged) {
for (var project : ProjectManager.getInstance().getOpenProjects()) {
if (!project.isDisposed()) {
ViewService viewService = project.getService(ViewService.class);
if (viewService != null && !viewService.isDisposed()) {
for (var project : ProjectManager.getInstance().getOpenProjects()) {
if (!project.isDisposed()) {
ViewService viewService = project.getService(ViewService.class);
if (viewService != null && !viewService.isDisposed()) {
if (tabColorsChanged) {
viewService.refreshFileColors();
}
if (workingTreeChanged) {
viewService.collectChanges(true);
}
}
}
}
Expand All @@ -65,6 +74,8 @@ public void reset() {
GitScopeSettings settings = GitScopeSettings.getInstance();
settingsComponent.setSeparateGutterRendering(settings.isSeparateGutterRendering());
settingsComponent.setScopeFileColors(settings.isScopeFileColors());
settingsComponent.setShowUntrackedFiles(settings.isShowUntrackedFiles());
settingsComponent.setShowDeletedFiles(settings.isShowDeletedFiles());
}

@Override
Expand Down