This is an automated email from the ASF dual-hosted git repository.

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 6e475d1eb7 issue #7520 : fix MissingObjectException when switching git 
projects (#7531)
6e475d1eb7 is described below

commit 6e475d1eb77f1f7ce48db525c47a163c03c003c0
Author: Matt Casters <[email protected]>
AuthorDate: Thu Jul 16 10:57:25 2026 +0200

    issue #7520 : fix MissingObjectException when switching git projects (#7531)
    
    Clear Git Perspective UI state before rebuild on root change, close the
    previous JGit repository when opening another, and treat foreign commit
    ids as not-in-branch instead of logging MissingObjectException.
---
 .../main/java/org/apache/hop/git/GitGuiPlugin.java | 10 ++-
 .../java/org/apache/hop/git/GitPerspective.java    | 82 +++++++++++++++++----
 .../main/java/org/apache/hop/git/model/UIGit.java  |  6 +-
 .../hop/git/GitPerspectiveBranchCheckTest.java     | 84 ++++++++++++++++++++++
 4 files changed, 164 insertions(+), 18 deletions(-)

diff --git 
a/plugins/misc/git/src/main/java/org/apache/hop/git/GitGuiPlugin.java 
b/plugins/misc/git/src/main/java/org/apache/hop/git/GitGuiPlugin.java
index 7ea2812d0d..8e019ff553 100644
--- a/plugins/misc/git/src/main/java/org/apache/hop/git/GitGuiPlugin.java
+++ b/plugins/misc/git/src/main/java/org/apache/hop/git/GitGuiPlugin.java
@@ -724,7 +724,15 @@ public class GitGuiPlugin
     //
     GitConfig config = GitConfigSingleton.getConfig();
 
-    git = null;
+    // Close the previous repository so pack/index handles are released 
(Windows + native git).
+    if (git != null) {
+      try {
+        git.closeRepo();
+      } catch (Exception e) {
+        LogChannel.UI.logError("Error closing previous git repository", e);
+      }
+      git = null;
+    }
     setBranchLabel(null);
 
     if (config.isEnabled()) {
diff --git 
a/plugins/misc/git/src/main/java/org/apache/hop/git/GitPerspective.java 
b/plugins/misc/git/src/main/java/org/apache/hop/git/GitPerspective.java
index c0ffcb7294..68e79a38fe 100644
--- a/plugins/misc/git/src/main/java/org/apache/hop/git/GitPerspective.java
+++ b/plugins/misc/git/src/main/java/org/apache/hop/git/GitPerspective.java
@@ -70,6 +70,7 @@ import org.eclipse.jgit.api.ListBranchCommand;
 import org.eclipse.jgit.api.ResetCommand;
 import org.eclipse.jgit.diff.DiffEntry;
 import org.eclipse.jgit.diff.RenameDetector;
+import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.lib.AnyObjectId;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
@@ -609,20 +610,56 @@ public class GitPerspective implements IHopPerspective {
    * @return {@code true} if the specified commit is in the current branch, 
{@code false} otherwise.
    */
   public boolean isCommitInCurrentBranch(RevCommit commit) {
+    if (commit == null) {
+      return false;
+    }
     try {
-      Git git = GitGuiPlugin.getInstance().getGit().getGit();
-      Repository repository = git.getRepository();
-      try (RevWalk walk = new RevWalk(repository)) {
-        RevCommit headCommit = 
walk.parseCommit(repository.resolve(Constants.HEAD));
-        RevCommit commitToCheck = walk.parseCommit(commit.getId());
-        return walk.isMergedInto(commitToCheck, headCommit);
+      UIGit uiGit = GitGuiPlugin.getInstance().getGit();
+      if (uiGit == null) {
+        return false;
       }
+      return isCommitInCurrentBranch(uiGit.getGit().getRepository(), 
commit.getId());
+    } catch (MissingObjectException e) {
+      // Object id not present in the currently open repository (e.g. after 
project switch)
+      return false;
     } catch (Exception e) {
       LogChannel.UI.logError("Error checking if commit is in current branch", 
e);
       return false;
     }
   }
 
+  /**
+   * Whether {@code commitId} is reachable from HEAD in {@code repository}.
+   *
+   * <p>Returns {@code false} when the id is missing from this object database 
(for example a stale
+   * history selection left over from another project after a repository 
switch). Does not require
+   * SWT or HopGui.
+   *
+   * @param repository repository to check against (may be {@code null})
+   * @param commitId commit object id (may be {@code null})
+   * @return {@code true} if the commit is merged into HEAD
+   * @throws Exception if a non-missing-object git error occurs while walking 
history
+   */
+  static boolean isCommitInCurrentBranch(Repository repository, AnyObjectId 
commitId)
+      throws Exception {
+    if (repository == null || commitId == null) {
+      return false;
+    }
+    // Stale selection from a previous project/repo must not be treated as a 
hard error
+    if (!repository.getObjectDatabase().has(commitId)) {
+      return false;
+    }
+    try (RevWalk walk = new RevWalk(repository)) {
+      ObjectId headId = repository.resolve(Constants.HEAD);
+      if (headId == null) {
+        return false;
+      }
+      RevCommit headCommit = walk.parseCommit(headId);
+      RevCommit commitToCheck = walk.parseCommit(commitId);
+      return walk.isMergedInto(commitToCheck, headCommit);
+    }
+  }
+
   void startFetchAutomaticTimer() {
     if (!GitConfigSingleton.getConfig().isFetchAutomatic()) {
       stopFetchAutomaticTimer();
@@ -1614,19 +1651,16 @@ public class GitPerspective implements IHopPerspective {
 
       UIGit uiGit = GitGuiPlugin.getInstance().getGit();
 
-      updateGui();
+      // Drop UI state bound to the previous repository before reading 
selection / rebuilding.
+      // Otherwise updateGui() would parse stale RevCommit ids against the 
newly opened repo
+      // (MissingObjectException after project switch). See issue #7520.
+      clearGitUiState();
 
-      // If Git is not used
       if (uiGit == null) {
-        wRefTree.removeAll();
-        wHistoryTable.removeAll();
-        wFileTree.removeAll();
-        setDiffText(null);
+        updateGui();
         return;
       }
 
-      Git git = uiGit.getGit();
-
       // Refresh the file explorer perspective (file tree, change colors...)
       // Refresh the metadata perspective (if file metadata provider)
       if (refreshAll) {
@@ -1634,13 +1668,31 @@ public class GitPerspective implements IHopPerspective {
         MetadataPerspective.getInstance().refresh();
       }
 
-      refreshRef(git);
+      refreshRef(uiGit.getGit());
       refreshHistory(Constants.HEAD);
+      updateGui();
     } catch (Exception e) {
       LogChannel.UI.logError("Error refresh git history", e);
     }
   }
 
+  /**
+   * Clears ref/history/file widgets and the diff view. Must run before 
rebuilding after a
+   * repository switch so no RevCommit/Ref from the previous repo remains 
selected.
+   */
+  protected void clearGitUiState() {
+    if (wRefTree != null && !wRefTree.isDisposed()) {
+      wRefTree.removeAll();
+    }
+    if (wHistoryTable != null && !wHistoryTable.isDisposed()) {
+      wHistoryTable.removeAll();
+    }
+    if (wFileTree != null && !wFileTree.isDisposed()) {
+      wFileTree.removeAll();
+    }
+    setDiffText(null);
+  }
+
   protected void refreshRef(Git git) {
     try {
       wRefTree.setRedraw(false);
diff --git a/plugins/misc/git/src/main/java/org/apache/hop/git/model/UIGit.java 
b/plugins/misc/git/src/main/java/org/apache/hop/git/model/UIGit.java
index cc22981b42..eb79851d5c 100644
--- a/plugins/misc/git/src/main/java/org/apache/hop/git/model/UIGit.java
+++ b/plugins/misc/git/src/main/java/org/apache/hop/git/model/UIGit.java
@@ -501,8 +501,10 @@ public class UIGit extends VCS {
   }
 
   public void closeRepo() {
-    git.close();
-    git = null;
+    if (git != null) {
+      git.close();
+      git = null;
+    }
   }
 
   public void add(String filePattern) throws HopException {
diff --git 
a/plugins/misc/git/src/test/java/org/apache/hop/git/GitPerspectiveBranchCheckTest.java
 
b/plugins/misc/git/src/test/java/org/apache/hop/git/GitPerspectiveBranchCheckTest.java
new file mode 100644
index 0000000000..7e5c5923e8
--- /dev/null
+++ 
b/plugins/misc/git/src/test/java/org/apache/hop/git/GitPerspectiveBranchCheckTest.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.git;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import org.apache.commons.io.FileUtils;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.junit.RepositoryTestCase;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.junit.Test;
+
+/**
+ * Headless checks for {@link 
GitPerspective#isCommitInCurrentBranch(Repository,
+ * org.eclipse.jgit.lib.AnyObjectId)} (issue #7520). Uses pure JGit temp 
repositories — no native
+ * git binary and no SWT.
+ */
+public class GitPerspectiveBranchCheckTest extends RepositoryTestCase {
+
+  @Test
+  public void commitOnCurrentBranchIsDetected() throws Exception {
+    try (Git git = new Git(db)) {
+      writeTrashFile("a.txt", "hello");
+      git.add().addFilepattern("a.txt").call();
+      RevCommit commit = git.commit().setMessage("initial").call();
+
+      assertTrue(GitPerspective.isCommitInCurrentBranch(db, commit));
+    }
+  }
+
+  @Test
+  public void commitFromOtherRepositoryReturnsFalse() throws Exception {
+    // Repo A: commit that will be "stale selection" when checking against B
+    RevCommit commitFromA;
+    try (Git gitA = new Git(db)) {
+      writeTrashFile("a.txt", "from-A");
+      gitA.add().addFilepattern("a.txt").call();
+      commitFromA = gitA.commit().setMessage("commit in A").call();
+    }
+
+    // Repo B: different history; does not contain A's objects
+    try (Repository dbB = createWorkRepository();
+        Git gitB = new Git(dbB)) {
+      File bFile = new File(dbB.getWorkTree(), "b.txt");
+      FileUtils.writeStringToFile(bFile, "from-B", StandardCharsets.UTF_8);
+      gitB.add().addFilepattern("b.txt").call();
+      gitB.commit().setMessage("commit in B").call();
+
+      // Core #7520 regression: foreign object id must not throw 
MissingObjectException
+      assertFalse(GitPerspective.isCommitInCurrentBranch(dbB, commitFromA));
+    }
+  }
+
+  @Test
+  public void nullArgumentsReturnFalse() throws Exception {
+    assertFalse(GitPerspective.isCommitInCurrentBranch(null, null));
+    try (Git git = new Git(db)) {
+      writeTrashFile("a.txt", "x");
+      git.add().addFilepattern("a.txt").call();
+      RevCommit commit = git.commit().setMessage("c").call();
+      assertFalse(GitPerspective.isCommitInCurrentBranch(null, commit));
+      assertFalse(GitPerspective.isCommitInCurrentBranch(db, null));
+    }
+  }
+}

Reply via email to