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
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,15 @@ public void rootChanged(String rootFolder, String rootName) {
//
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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
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;
Expand Down Expand Up @@ -586,20 +587,56 @@ public String getSelectedFile() {
* @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();
Expand Down Expand Up @@ -1592,33 +1629,48 @@ public void refresh(boolean refreshAll) {

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) {
ExplorerPerspective.getInstance().refresh();
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,10 @@ public void openRepo(String baseDirectory) throws Exception {
}

public void closeRepo() {
git.close();
git = null;
if (git != null) {
git.close();
git = null;
}
}

public void add(String filePattern) throws HopException {
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}
}
}
Loading