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 f2527ef036 fix git diff and overall experience of Git info window, 
fixes #6051 (#6349)
f2527ef036 is described below

commit f2527ef03656c2a223d4365b64f3ad2437c378ed
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Mon Jan 12 17:08:11 2026 +0100

    fix git diff and overall experience of Git info window, fixes #6051 (#6349)
---
 .../git/info/GitInfoExplorerFileTypeHandler.java   | 208 ++++++++++++++++++---
 .../main/java/org/apache/hop/git/model/UIGit.java  | 149 +++++++++++----
 2 files changed, 294 insertions(+), 63 deletions(-)

diff --git 
a/plugins/misc/git/src/main/java/org/apache/hop/git/info/GitInfoExplorerFileTypeHandler.java
 
b/plugins/misc/git/src/main/java/org/apache/hop/git/info/GitInfoExplorerFileTypeHandler.java
index bd9663958a..8a25092da9 100644
--- 
a/plugins/misc/git/src/main/java/org/apache/hop/git/info/GitInfoExplorerFileTypeHandler.java
+++ 
b/plugins/misc/git/src/main/java/org/apache/hop/git/info/GitInfoExplorerFileTypeHandler.java
@@ -195,20 +195,34 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     ColumnInfo[] revisionColumns = {
       new ColumnInfo(
           BaseMessages.getString(PKG, 
"GitInfoDialog.Revisions.ColumnRevision.Label"),
-          ColumnInfo.COLUMN_TYPE_TEXT),
+          ColumnInfo.COLUMN_TYPE_TEXT,
+          false,
+          true),
       new ColumnInfo(
           BaseMessages.getString(PKG, 
"GitInfoDialog.Revisions.ColumnCreation.Label"),
-          ColumnInfo.COLUMN_TYPE_TEXT),
+          ColumnInfo.COLUMN_TYPE_TEXT,
+          false,
+          true),
       new ColumnInfo(
           BaseMessages.getString(PKG, 
"GitInfoDialog.Revisions.ColumnLogin.Label"),
-          ColumnInfo.COLUMN_TYPE_TEXT),
+          ColumnInfo.COLUMN_TYPE_TEXT,
+          false,
+          true),
       new ColumnInfo(
           BaseMessages.getString(PKG, 
"GitInfoDialog.Revisions.ColumnComment.Label"),
-          ColumnInfo.COLUMN_TYPE_TEXT),
+          ColumnInfo.COLUMN_TYPE_TEXT,
+          false,
+          true),
     };
     wRevisions =
         new TableView(
-            hopGui.getVariables(), composite, SWT.BORDER, revisionColumns, 1, 
null, props);
+            hopGui.getVariables(),
+            composite,
+            SWT.BORDER | SWT.SINGLE,
+            revisionColumns,
+            1,
+            null,
+            props);
     wRevisions.setReadonly(true);
     PropsUi.setLook(wRevisions);
     FormData fdRevisions = new FormData();
@@ -217,7 +231,18 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     fdRevisions.right = new FormAttachment(100, 0);
     fdRevisions.bottom = new FormAttachment(40, 0);
     wRevisions.setLayoutData(fdRevisions);
-    wRevisions.table.addListener(SWT.Selection, e -> refreshChangedFiles());
+    // Use MouseDown event instead of Selection to ensure the click is fully 
processed
+    wRevisions.table.addListener(
+        SWT.MouseDown,
+        e ->
+            // Delay slightly to ensure selection is registered
+            
wRevisions.table.getDisplay().asyncExec(this::refreshChangedFiles));
+    // Also handle keyboard navigation (arrow keys, etc.)
+    wRevisions.table.addListener(
+        SWT.KeyDown,
+        e ->
+            // Delay slightly to ensure selection is registered
+            
wRevisions.table.getDisplay().asyncExec(this::refreshChangedFiles));
     lastControl = wRevisions;
 
     Label wlFiles = new Label(composite, SWT.LEFT | SWT.SINGLE);
@@ -244,20 +269,37 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     ColumnInfo[] filesColumns = {
       new ColumnInfo(
           BaseMessages.getString(PKG, 
"GitInfoDialog.ChangedFiles.Filename.Label"),
-          ColumnInfo.COLUMN_TYPE_TEXT),
+          ColumnInfo.COLUMN_TYPE_TEXT,
+          false,
+          true), // not numeric, read-only
       new ColumnInfo(
           BaseMessages.getString(PKG, 
"GitInfoDialog.ChangedFiles.Status.Label"),
-          ColumnInfo.COLUMN_TYPE_TEXT),
+          ColumnInfo.COLUMN_TYPE_TEXT,
+          false,
+          true), // not numeric, read-only
       new ColumnInfo(
           BaseMessages.getString(PKG, 
"GitInfoDialog.ChangedFiles.Staged.Label"),
-          ColumnInfo.COLUMN_TYPE_CCOMBO,
-          new String[] {"Y", "N"}),
+          ColumnInfo.COLUMN_TYPE_TEXT,
+          false,
+          true), // not numeric, read-only - use TEXT not CCOMBO for true 
read-only
     };
     wFiles =
-        new TableView(hopGui.getVariables(), sashForm, SWT.BORDER, 
filesColumns, 1, null, props);
+        new TableView(
+            hopGui.getVariables(), sashForm, SWT.BORDER | SWT.SINGLE, 
filesColumns, 1, null, props);
     wFiles.setReadonly(true);
     PropsUi.setLook(wFiles);
-    wFiles.table.addListener(SWT.Selection, e -> fileSelected());
+    // Use MouseDown event instead of Selection to ensure the click is fully 
processed
+    wFiles.table.addListener(
+        SWT.MouseDown,
+        e ->
+            // Delay slightly to ensure selection is registered
+            wFiles.table.getDisplay().asyncExec(this::fileSelected));
+    // Also handle keyboard navigation (arrow keys, etc.)
+    wFiles.table.addListener(
+        SWT.KeyDown,
+        e ->
+            // Delay slightly to ensure selection is registered
+            wFiles.table.getDisplay().asyncExec(this::fileSelected));
 
     Composite wDiffComposite = new Composite(sashForm, SWT.NONE);
     PropsUi.setLook(wDiffComposite);
@@ -291,7 +333,7 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     fdDiff.bottom = new FormAttachment(100, 0);
     wDiff.setLayoutData(fdDiff);
 
-    sashForm.setWeights(new int[] {40, 60});
+    sashForm.setWeights(40, 60);
 
     refresh();
 
@@ -527,8 +569,20 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     UIGit git = guiPlugin.getGit();
     List<ObjectRevision> revisions = new ArrayList<>();
     try {
-      String relativePath =
-          calculateRelativePath(perspective.getRootFolder(), 
explorerFile.getFilename());
+      // Use the git repository root, not the perspective root folder
+      // The git repository root is what JGit needs for relative paths
+      String gitRoot = git.getDirectory();
+      String relativePath = calculateRelativePath(gitRoot, 
explorerFile.getFilename());
+      LogChannel.UI.logDebug(
+          "GitInfo refresh - gitRoot: '"
+              + gitRoot
+              + "', perspectiveRoot: '"
+              + perspective.getRootFolder()
+              + "', file: '"
+              + explorerFile.getFilename()
+              + "', relative: '"
+              + relativePath
+              + "'");
       revisions = git.getRevisions(relativePath);
     } catch (Exception e) {
       LogChannel.UI.logError(
@@ -548,13 +602,30 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
       item.setText(4, Const.NVL(revision.getComment(), ""));
     }
     wRevisions.optimizeTableView();
-    if (!revisions.isEmpty()) {
-      // Select the first line
-      wRevisions.setSelection(new int[] {0});
-    }
     wbDiff.setEnabled(false);
 
+    // Refresh changed files first, before selecting a revision
     refreshChangedFiles();
+
+    // Select the first revision after the UI is fully rendered
+    // Use asyncExec to ensure the table is ready to handle the selection
+    if (!revisions.isEmpty()) {
+      parentComposite
+          .getDisplay()
+          .asyncExec(
+              () -> {
+                if (!wRevisions.isDisposed() && 
wRevisions.table.getItemCount() > 0) {
+                  wRevisions.table.setSelection(0);
+                  wRevisions.table.showSelection();
+                  LogChannel.UI.logDebug(
+                      "GitInfo refresh: Auto-selected first revision (index 0) 
after UI render");
+
+                  // Refresh changed files now that a revision is selected
+                  // This will trigger auto-selection of the file if there's 
only one
+                  refreshChangedFiles();
+                }
+              });
+    }
   }
 
   private String calculateRelativePath(String rootFolder, String filename)
@@ -562,10 +633,21 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     FileObject root = HopVfs.getFileObject(rootFolder);
     FileObject file = HopVfs.getFileObject(filename);
 
-    return root.getName().getRelativeName(file.getName());
+    String relativePath = root.getName().getRelativeName(file.getName());
+
+    // Normalize for JGit: forward slashes, no leading slash
+    if (relativePath != null && !".".equals(relativePath)) {
+      relativePath = relativePath.replace("\\", "/");
+      if (relativePath.startsWith("/")) {
+        relativePath = relativePath.substring(1);
+      }
+    }
+
+    return relativePath;
   }
 
   private void fileSelected() {
+    LogChannel.UI.logDebug("fileSelected: File clicked in changed files list");
     String filename = showFileDiff();
     wbDiff.setEnabled(false);
 
@@ -573,10 +655,11 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
       // Enable visual diff button?
       //
       if (filename != null) {
+        LogChannel.UI.logDebug("fileSelected: Diff generated for file: " + 
filename);
         // if a folder is selected in the left pane then return
         ExplorerPerspective perspective = HopGui.getExplorerPerspective();
         if 
(!perspective.getPipelineFileType().isHandledBy(explorerFile.getFilename(), 
false)
-            & 
!perspective.getWorkflowFileType().isHandledBy(explorerFile.getFilename(), 
false)) {
+            && 
!perspective.getWorkflowFileType().isHandledBy(explorerFile.getFilename(), 
false)) {
           return;
         }
 
@@ -603,10 +686,12 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     GitGuiPlugin guiPlugin = GitGuiPlugin.getInstance();
     UIGit git = guiPlugin.getGit();
 
-    if (wRevisions.getSelectionIndices().length == 0) {
+    if (wRevisions.table.getSelectionCount() == 0) {
+      LogChannel.UI.logDebug("showFileDiff: No revision selected");
       return null;
     }
-    if (wFiles.getSelectionIndices().length == 0) {
+    if (wFiles.table.getSelectionCount() == 0) {
+      LogChannel.UI.logDebug("showFileDiff: No file selected");
       return null;
     }
 
@@ -614,7 +699,12 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
 
     // A revision/commit was selected...
     //
-    TableItem revisionItem = wRevisions.table.getSelection()[0];
+    TableItem[] revisionSelection = wRevisions.table.getSelection();
+    if (revisionSelection.length == 0) {
+      LogChannel.UI.logDebug("showFileDiff: Revision selection array is 
empty");
+      return null;
+    }
+    TableItem revisionItem = revisionSelection[0];
     String revisionId = revisionItem.getText(1);
     boolean workingTree = VCS.WORKINGTREE.equals(revisionId);
 
@@ -648,13 +738,21 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
     String rootFolder = git.getDirectory();
     boolean showStaged = true;
 
-    // Cleanup the diff text field
+    // Clear the diff text field and disable the visual diff button
     wDiff.setText("");
+    wbDiff.setEnabled(false);
 
     // Pick up the revision ID...
     //
     if (wRevisions.table.getSelectionCount() == 0) {
-      changedFiles = new ArrayList<>(guiPlugin.getChangedFiles().values());
+      // No revision selected yet (during initial load)
+      // Still filter by the selected file/folder
+      changedFiles = new ArrayList<>();
+      for (UIFile changedFile : guiPlugin.getChangedFiles().values()) {
+        if (isFilteredPath(rootFolder, changedFile.getName(), selectedFile)) {
+          changedFiles.add(changedFile);
+        }
+      }
     } else {
       String revisionId = wRevisions.table.getSelection()[0].getText(1);
       String parentRevisionId =
@@ -720,6 +818,40 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
       }
     }
     wFiles.optimizeTableView();
+
+    // Auto-select file and show diff in certain cases
+    boolean shouldAutoSelect = false;
+
+    if (changedFiles.size() == 1 && wRevisions.table.getSelectionCount() > 0) {
+      // Single file mode - always auto-select
+      shouldAutoSelect = true;
+    } else if (!changedFiles.isEmpty() && wFiles.table.getSelectionCount() > 
0) {
+      // Multiple files but one was previously selected - try to keep that 
selection or select first
+      shouldAutoSelect = true;
+    }
+
+    if (shouldAutoSelect) {
+      wFiles
+          .table
+          .getDisplay()
+          .asyncExec(
+              () -> {
+                if (!wFiles.isDisposed() && wFiles.table.getItemCount() > 0) {
+                  // If no selection, or single file mode, select the first 
file
+                  if (wFiles.table.getSelectionCount() == 0 || 
wFiles.table.getItemCount() == 1) {
+                    wFiles.table.setSelection(0);
+                    wFiles.table.showSelection();
+                    LogChannel.UI.logDebug("refreshChangedFiles: Auto-selected 
file at index 0");
+                  }
+                  // Show the diff for the selected file
+                  if (wFiles.table.getSelectionCount() > 0) {
+                    fileSelected();
+                    LogChannel.UI.logDebug(
+                        "refreshChangedFiles: Triggered diff display for 
selected file");
+                  }
+                }
+              });
+    }
   }
 
   /**
@@ -733,11 +865,33 @@ public class GitInfoExplorerFileTypeHandler extends 
BaseExplorerFileTypeHandler
   private boolean isFilteredPath(String root, String path, String 
selectedFile) {
     try {
       String relativeSelected = calculateRelativePath(root, selectedFile);
+      LogChannel.UI.logDebug(
+          "isFilteredPath: path='"
+              + path
+              + "', relativeSelected='"
+              + relativeSelected
+              + "', selectedFile='"
+              + selectedFile
+              + "'");
+
       if (".".equals(relativeSelected)) {
         return true; // path is whole project
       }
-      return path.startsWith(relativeSelected);
+
+      // Check if the selected file is a directory
+      FileObject selectedFileObject = HopVfs.getFileObject(selectedFile);
+      boolean isDirectory = selectedFileObject.isFolder();
+
+      if (isDirectory) {
+        // For a folder, check if the path is in that folder or subfolder
+        // Use startsWith with a trailing slash to avoid false matches
+        return path.equals(relativeSelected) || 
path.startsWith(relativeSelected + "/");
+      } else {
+        // For a file, only exact match
+        return path.equals(relativeSelected);
+      }
     } catch (Exception e) {
+      LogChannel.UI.logError("Error in isFilteredPath", e);
       return 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 7e85c7cddc..e7129ee44b 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
@@ -331,15 +331,55 @@ public class UIGit extends VCS {
   public List<ObjectRevision> getRevisions(String path) {
     List<ObjectRevision> revisions = new ArrayList<>();
     try {
+      // Normalize the path for JGit (forward slashes, no leading slash)
+      String normalizedPath = normalizePathForJGit(path);
+      if (path != null && !".".equals(path)) {
+        LogChannel.UI.logDebug(
+            "Getting revisions for path - original: '"
+                + path
+                + "', normalized: '"
+                + normalizedPath
+                + "'");
+      }
+
+      // Check if there are working tree changes for the specific path
+      boolean hasWorkingTreeChanges = false;
       if (!isClean()
           || git.getRepository().getRepositoryState() == 
RepositoryState.MERGING_RESOLVED) {
-        GitObjectRevision rev =
-            new GitObjectRevision(WORKINGTREE, "*", new Date(), " // " + 
VCS.WORKINGTREE);
-        revisions.add(rev);
+        // If a specific path is provided, check if that path has changes
+        if (normalizedPath != null && !".".equals(normalizedPath)) {
+          List<UIFile> stagedFiles = getStagedFiles();
+          List<UIFile> unstagedFiles = getUnstagedFiles();
+          for (UIFile file : stagedFiles) {
+            if (file.getName().equals(normalizedPath)
+                || file.getName().startsWith(normalizedPath + "/")) {
+              hasWorkingTreeChanges = true;
+              break;
+            }
+          }
+          if (!hasWorkingTreeChanges) {
+            for (UIFile file : unstagedFiles) {
+              if (file.getName().equals(normalizedPath)
+                  || file.getName().startsWith(normalizedPath + "/")) {
+                hasWorkingTreeChanges = true;
+                break;
+              }
+            }
+          }
+        } else {
+          // No specific path, so there are working tree changes
+          hasWorkingTreeChanges = true;
+        }
+
+        if (hasWorkingTreeChanges) {
+          GitObjectRevision rev =
+              new GitObjectRevision(WORKINGTREE, "*", new Date(), " // " + 
VCS.WORKINGTREE);
+          revisions.add(rev);
+        }
       }
       LogCommand logCommand = git.log();
-      if (path != null && !".".equals(path)) {
-        logCommand = logCommand.addPath(path);
+      if (normalizedPath != null && !".".equals(normalizedPath)) {
+        logCommand = logCommand.addPath(normalizedPath);
       }
       Iterable<RevCommit> iterable = logCommand.call();
       for (RevCommit commit : iterable) {
@@ -352,7 +392,7 @@ public class UIGit extends VCS {
         revisions.add(rev);
       }
     } catch (Exception e) {
-      // Do nothing
+      LogChannel.UI.logError("Error getting git revisions for path: " + path, 
e);
     }
     return revisions;
   }
@@ -365,9 +405,10 @@ public class UIGit extends VCS {
     List<UIFile> files = new ArrayList<>();
     Status status = null;
     try {
+      String normalizedPath = normalizePathForJGit(path);
       StatusCommand statusCommand = git.status();
-      if (path != null && !".".equals(path)) {
-        statusCommand = statusCommand.addPath(path);
+      if (normalizedPath != null && !".".equals(normalizedPath)) {
+        statusCommand = statusCommand.addPath(normalizedPath);
       }
 
       status = statusCommand.call();
@@ -445,18 +486,19 @@ public class UIGit extends VCS {
 
   public void add(String filePattern) throws HopException {
     try {
-      if (filePattern.endsWith(CONST_OURS) || 
filePattern.endsWith(CONST_THEIRS)) {
+      String normalizedPattern = normalizePathForJGit(filePattern);
+      if (normalizedPattern.endsWith(CONST_OURS) || 
normalizedPattern.endsWith(CONST_THEIRS)) {
         FileUtils.rename(
-            new File(directory, filePattern),
-            new File(directory, FilenameUtils.removeExtension(filePattern)),
+            new File(directory, normalizedPattern),
+            new File(directory, 
FilenameUtils.removeExtension(normalizedPattern)),
             StandardCopyOption.REPLACE_EXISTING);
-        filePattern = FilenameUtils.removeExtension(filePattern);
+        normalizedPattern = FilenameUtils.removeExtension(normalizedPattern);
         org.apache.commons.io.FileUtils.deleteQuietly(
-            new File(directory, filePattern + CONST_OURS));
+            new File(directory, normalizedPattern + CONST_OURS));
         org.apache.commons.io.FileUtils.deleteQuietly(
-            new File(directory, filePattern + CONST_THEIRS));
+            new File(directory, normalizedPattern + CONST_THEIRS));
       }
-      git.add().addFilepattern(filePattern).call();
+      git.add().addFilepattern(normalizedPattern).call();
     } catch (Exception e) {
       throw new HopException("Error adding '" + filePattern + "'to git", e);
     }
@@ -464,7 +506,8 @@ public class UIGit extends VCS {
 
   public void rm(String filepattern) {
     try {
-      git.rm().addFilepattern(filepattern).call();
+      String normalizedPattern = normalizePathForJGit(filepattern);
+      git.rm().addFilepattern(normalizedPattern).call();
     } catch (Exception e) {
       showMessageBox(BaseMessages.getString(PKG, CONST_DIALOG_ERROR), 
e.getMessage());
     }
@@ -482,7 +525,8 @@ public class UIGit extends VCS {
   /** Reset a file to HEAD (mixed) */
   public void resetPath(String path) {
     try {
-      git.reset().addPath(path).call();
+      String normalizedPath = normalizePathForJGit(path);
+      git.reset().addPath(normalizedPath).call();
     } catch (Exception e) {
       showMessageBox(BaseMessages.getString(PKG, CONST_DIALOG_ERROR), 
e.getMessage());
     }
@@ -689,9 +733,11 @@ public class UIGit extends VCS {
   public String diff(String oldCommitId, String newCommitId, String file) {
     ByteArrayOutputStream out = new ByteArrayOutputStream();
     try {
+      String normalizedFile = normalizePathForJGit(file);
       getDiffCommand(oldCommitId, newCommitId)
           .setOutputStream(out)
-          .setPathFilter(file == null ? TreeFilter.ALL : 
PathFilter.create(file))
+          .setPathFilter(
+              normalizedFile == null ? TreeFilter.ALL : 
PathFilter.create(normalizedFile))
           .call();
       return out.toString(StandardCharsets.UTF_8);
     } catch (Exception e) {
@@ -700,9 +746,10 @@ public class UIGit extends VCS {
   }
 
   public InputStream open(String file, String commitId) throws HopException {
+    String normalizedFile = normalizePathForJGit(file);
     if (commitId.equals(WORKINGTREE)) {
       String baseDirectory = getDirectory();
-      String filePath = baseDirectory + Const.FILE_SEPARATOR + file;
+      String filePath = baseDirectory + Const.FILE_SEPARATOR + normalizedFile;
       try {
         return HopVfs.getInputStream(filePath);
       } catch (HopFileException e) {
@@ -713,23 +760,29 @@ public class UIGit extends VCS {
     RevTree tree = commit.getTree();
     try (TreeWalk tw = new TreeWalk(git.getRepository())) {
       tw.addTree(tree);
-      tw.setFilter(PathFilter.create(file));
+      tw.setFilter(PathFilter.create(normalizedFile));
       tw.setRecursive(true);
       tw.next();
       ObjectLoader loader = git.getRepository().open(tw.getObjectId(0));
       return loader.openStream();
     } catch (MissingObjectException e) {
       throw new HopException(
-          "Unable to find file '" + file + CONST_FOR_COMMIT_ID + commitId + 
"", e);
+          "Unable to find file '" + normalizedFile + CONST_FOR_COMMIT_ID + 
commitId + "", e);
     } catch (IncorrectObjectTypeException e) {
       throw new HopException(
-          "Incorrect object type error for file '" + file + 
CONST_FOR_COMMIT_ID + commitId + "", e);
+          "Incorrect object type error for file '"
+              + normalizedFile
+              + CONST_FOR_COMMIT_ID
+              + commitId
+              + "",
+          e);
     } catch (CorruptObjectException e) {
       throw new HopException(
-          "Corrupt object error for file '" + file + CONST_FOR_COMMIT_ID + 
commitId + "", e);
+          "Corrupt object error for file '" + normalizedFile + 
CONST_FOR_COMMIT_ID + commitId + "",
+          e);
     } catch (IOException e) {
       throw new HopException(
-          "Error reading git file '" + file + CONST_FOR_COMMIT_ID + commitId + 
"", e);
+          "Error reading git file '" + normalizedFile + CONST_FOR_COMMIT_ID + 
commitId + "", e);
     }
   }
 
@@ -776,20 +829,21 @@ public class UIGit extends VCS {
 
   public void revertPath(String path) throws HopException {
     try {
+      String normalizedPath = normalizePathForJGit(path);
       // Revert files to HEAD state
-      Status status = git.status().addPath(path).call();
+      Status status = git.status().addPath(normalizedPath).call();
       if (!status.getUntracked().isEmpty() || !status.getAdded().isEmpty()) {
-        resetPath(path);
-        org.apache.commons.io.FileUtils.deleteQuietly(new File(directory, 
path));
+        resetPath(normalizedPath);
+        org.apache.commons.io.FileUtils.deleteQuietly(new File(directory, 
normalizedPath));
       }
 
       /*
        * This is a work-around to discard changes of conflicting files
        * Git CLI `git checkout -- conflicted.txt` discards the changes, but 
jgit does not
        */
-      git.add().addFilepattern(path).call();
+      git.add().addFilepattern(normalizedPath).call();
 
-      git.checkout().setStartPoint(Constants.HEAD).addPath(path).call();
+      
git.checkout().setStartPoint(Constants.HEAD).addPath(normalizedPath).call();
       org.apache.commons.io.FileUtils.deleteQuietly(new File(directory, path + 
CONST_OURS));
       org.apache.commons.io.FileUtils.deleteQuietly(new File(directory, path + 
CONST_THEIRS));
     } catch (Exception e) {
@@ -806,9 +860,10 @@ public class UIGit extends VCS {
   public List<String> getRevertPathFiles(String path) throws HopException {
     try {
       Set<String> files = new HashSet<>();
+      String normalizedPath = normalizePathForJGit(path);
       StatusCommand statusCommand = git.status();
-      if (path != null && !".".equals(path)) {
-        statusCommand = statusCommand.addPath(path);
+      if (normalizedPath != null && !".".equals(normalizedPath)) {
+        statusCommand = statusCommand.addPath(normalizedPath);
       }
 
       // Get files to be reverted to HEAD state
@@ -898,15 +953,16 @@ public class UIGit extends VCS {
   }
 
   private void checkout(String path, String commitId, String postfix) throws 
HopException {
-    InputStream stream = open(path, commitId);
-    File file = new File(directory + Const.FILE_SEPARATOR + path + postfix);
+    String normalizedPath = normalizePathForJGit(path);
+    InputStream stream = open(normalizedPath, commitId);
+    File file = new File(directory + Const.FILE_SEPARATOR + normalizedPath + 
postfix);
     try {
       org.apache.commons.io.FileUtils.copyInputStreamToFile(stream, file);
       stream.close();
     } catch (IOException e) {
       throw new HopException(
           "Error checking out file '"
-              + path
+              + normalizedPath
               + CONST_FOR_COMMIT_ID
               + commitId
               + "' and postfix "
@@ -946,6 +1002,26 @@ public class UIGit extends VCS {
     }
   }
 
+  /**
+   * Normalize a file path for JGit operations. JGit requires paths to: - Use 
forward slashes (/) as
+   * separators - Be relative to the repository root - Not start with a slash
+   *
+   * @param path The path to normalize (can be null)
+   * @return The normalized path, or the original if it's null or "."
+   */
+  private String normalizePathForJGit(String path) {
+    if (path == null || ".".equals(path)) {
+      return path;
+    }
+    // Convert backslashes to forward slashes
+    String normalized = path.replace("\\", "/");
+    // Remove leading slash if present
+    if (normalized.startsWith("/")) {
+      normalized = normalized.substring(1);
+    }
+    return normalized;
+  }
+
   public String getShortenedName(String name) {
     if (name.length() == Constants.OBJECT_ID_STRING_LENGTH) {
       return name.substring(0, 7);
@@ -1041,9 +1117,10 @@ public class UIGit extends VCS {
 
   public Set<String> getIgnored(String path) {
     try {
+      String normalizedPath = normalizePathForJGit(path);
       StatusCommand statusCommand = git.status();
-      if (path != null && !".".equals(path)) {
-        statusCommand = statusCommand.addPath(path);
+      if (normalizedPath != null && !".".equals(normalizedPath)) {
+        statusCommand = statusCommand.addPath(normalizedPath);
       }
       Status status = statusCommand.call();
       return status.getIgnoredNotInIndex();

Reply via email to