From d72d93493afcbb11591e21f2591e8a36ed44d634 Mon Sep 17 00:00:00 2001 From: Ondro Mihalyi Date: Sat, 4 Apr 2026 00:09:18 +0200 Subject: [PATCH] Optimization of Git status (in Commit dialog) Speeds up GitClient.getStatus() by deferring expensive evaluation of object Ids, which often compute file content hash, to evaluate them lazily only when needed. Additionally, skips calling isEntryIgnored, which recursively scans for .gitignore files up to the root directory, in case it's not needed at all. This saves additional few hundres of milliseconds. On Netbeans repository with a lot of files, this speeds up GitClient.getStatus() execution from 4 seconds to 1 second. --- .../libs/git/jgit/commands/StatusCommand.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/ide/libs.git/src/org/netbeans/libs/git/jgit/commands/StatusCommand.java b/ide/libs.git/src/org/netbeans/libs/git/jgit/commands/StatusCommand.java index fd59d4b717d5..b4a8e62904e1 100644 --- a/ide/libs.git/src/org/netbeans/libs/git/jgit/commands/StatusCommand.java +++ b/ide/libs.git/src/org/netbeans/libs/git/jgit/commands/StatusCommand.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.jgit.diff.DiffEntry; @@ -47,7 +48,9 @@ import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.treewalk.*; +import org.eclipse.jgit.treewalk.EmptyTreeIterator; +import org.eclipse.jgit.treewalk.FileTreeIterator; +import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.NotTreeFilter; import org.eclipse.jgit.treewalk.filter.OrTreeFilter; @@ -180,16 +183,21 @@ protected void run () throws GitException { DirCacheEntry indexEntry = indexIterator != null ? indexIterator.getDirCacheEntry() : null; boolean isFolder = false; if (!symlink && treeWalk.isSubtree()) { - if (mWorking == FileMode.TREE.getBits() && fti.isEntryIgnored()) { - if (mHead != 0 || mIndex != 0) { + if (mWorking == FileMode.TREE.getBits() && (mHead != 0 || mIndex != 0)) { + // directory exists in commit or index: always enter; mark as ignored if applicable + if (fti.isEntryIgnored()) { statusIndexWC = statusHeadWC = GitStatus.Status.STATUS_IGNORED; isFolder = true; treeWalk.enterSubtree(); } else { + treeWalk.enterSubtree(); + continue; + } + } else if (mWorking == FileMode.TREE.getBits() && fti.isEntryIgnored()) { Collection subTreeFilters = getSubtreeFilters(pathFilters, path); if (!subTreeFilters.isEmpty()) { // caller requested a status for a file under an ignored folder - treeWalk.setFilter(AndTreeFilter.create(treeWalk.getFilter(), OrTreeFilter.create(NotTreeFilter.create(PathFilter.create(path)), + treeWalk.setFilter(AndTreeFilter.create(treeWalk.getFilter(), OrTreeFilter.create(NotTreeFilter.create(PathFilter.create(path)), subTreeFilters.size() > 1 ? OrTreeFilter.create(subTreeFilters) : subTreeFilters.iterator().next()))); treeWalk.enterSubtree(); } @@ -200,7 +208,6 @@ protected void run () throws GitException { } else { continue; } - } } else { treeWalk.enterSubtree(); continue; @@ -217,11 +224,11 @@ protected void run () throws GitException { } else { statusIndexWC = GitStatus.Status.STATUS_ADDED; } - } else if (!isExistingSymlink(mIndex, mWorking) && (differ(mIndex, mWorking, checkExecutable) + } else if (!isExistingSymlink(mIndex, mWorking) && (differ(mIndex, mWorking, checkExecutable) || (mWorking != 0 && mWorking != FileMode.TREE.getBits() && fti.isModified(indexEntry, true, od))) || GitStatus.Status.STATUS_MODIFIED == getGitlinkStatus( - mWorking, treeWalk.getObjectId(T_WORKSPACE), - mIndex, treeWalk.getObjectId(T_INDEX))) { + mWorking, () -> treeWalk.getObjectId(T_WORKSPACE), + mIndex, () -> treeWalk.getObjectId(T_INDEX))) { statusIndexWC = GitStatus.Status.STATUS_MODIFIED; } else { statusIndexWC = GitStatus.Status.STATUS_NORMAL; @@ -237,8 +244,8 @@ protected void run () throws GitException { && (statusIndexWC != GitStatus.Status.STATUS_NORMAL || statusHeadIndex != GitStatus.Status.STATUS_NORMAL) && !treeWalk.getObjectId(T_COMMIT).equals(fti.getEntryObjectId()))) || GitStatus.Status.STATUS_MODIFIED == getGitlinkStatus( - mHead, treeWalk.getObjectId(T_WORKSPACE), - mHead, treeWalk.getObjectId(T_COMMIT))) { + mHead, () -> treeWalk.getObjectId(T_WORKSPACE), + mHead, () -> treeWalk.getObjectId(T_COMMIT))) { statusHeadWC = GitStatus.Status.STATUS_MODIFIED; } else { statusHeadWC = GitStatus.Status.STATUS_NORMAL; @@ -444,13 +451,13 @@ private void handleSymlink (List symLinks, String workTreePath) { } } - private GitStatus.Status getGitlinkStatus (int mode1, ObjectId id1, int mode2, ObjectId id2) { + private GitStatus.Status getGitlinkStatus (int mode1, Supplier id1, int mode2, Supplier id2) { if (mode1 == FileMode.TYPE_GITLINK || mode2 == FileMode.TYPE_GITLINK) { if (mode1 == FileMode.TYPE_MISSING) { return GitStatus.Status.STATUS_REMOVED; } else if (mode2 == FileMode.TYPE_MISSING) { return GitStatus.Status.STATUS_ADDED; - } else if (!id1.equals(id2)) { + } else if (!id1.get().equals(id2.get())) { return GitStatus.Status.STATUS_MODIFIED; } }