This is an automated email from the ASF dual-hosted git repository.
mattcasters 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 356212bb51 unstage files in the git commit perspective, fixes #7894.
(#7900)
356212bb51 is described below
commit 356212bb51ae138efe93415c8b21a355ecc4a92b
Author: Bart Maertens <[email protected]>
AuthorDate: Tue Aug 11 20:26:41 2026 +0200
unstage files in the git commit perspective, fixes #7894. (#7900)
---
.../ROOT/assets/images/icons/git-restore.svg | 3 +
.../ROOT/assets/images/icons/git-unstage.svg | 33 +++++
.../modules/ROOT/pages/hop-gui/hop-gui-git.adoc | 24 ++++
.../org/apache/hop/git/GitCommitPerspective.java | 134 ++++++++++++++++-----
.../misc/git/src/main/resources/git-unstage.svg | 33 +++++
.../hop/git/messages/messages_en_US.properties | 8 +-
.../java/org/apache/hop/git/model/UIGitTest.java | 54 +++++++++
7 files changed, 257 insertions(+), 32 deletions(-)
diff --git
a/docs/hop-user-manual/modules/ROOT/assets/images/icons/git-restore.svg
b/docs/hop-user-manual/modules/ROOT/assets/images/icons/git-restore.svg
new file mode 100644
index 0000000000..2824a03811
--- /dev/null
+++ b/docs/hop-user-manual/modules/ROOT/assets/images/icons/git-restore.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24
24">
+ <path fill="#0E3A5A" d="M20 13.5C20 17.09 17.09 20 13.5 20H6V18H13.5C16 18
18 16 18 13.5S16 9 13.5 9H7.83L10.91 12.09L9.5 13.5L4 8L9.5 2.5L10.92 3.91L7.83
7H13.5C17.09 7 20 9.91 20 13.5Z" />
+</svg>
\ No newline at end of file
diff --git
a/docs/hop-user-manual/modules/ROOT/assets/images/icons/git-unstage.svg
b/docs/hop-user-manual/modules/ROOT/assets/images/icons/git-unstage.svg
new file mode 100644
index 0000000000..ffc918179d
--- /dev/null
+++ b/docs/hop-user-manual/modules/ROOT/assets/images/icons/git-unstage.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ - 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.
+ -->
+
+<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="97px" height="97px" viewBox="0 0 97 97" enable-background="new
0 0 97 97" xml:space="preserve">
+<g transform="matrix(0.84298576,0,0,0.84298576,0.40113214,0.40160318)">
+ <path fill="#F05133"
d="M92.71,44.408L52.591,4.291c-2.31-2.311-6.057-2.311-8.369,0l-8.33,8.332L46.459,23.19
+
c2.456-0.829,5.272-0.273,7.229,1.686c1.969,1.97,2.521,4.81,1.67,7.275l10.187,10.185c2.465-0.85,5.306-0.3,7.275,1.671
+
c2.749,2.75,2.749,7.206,0,9.958c-2.752,2.751-7.208,2.751-9.962,0c-2.068-2.07-2.58-5.11-1.53-7.658l-9.501-9.499v24.998
+
c0.67,0.331,1.303,0.774,1.861,1.332c2.75,2.749,2.75,7.206,0,9.958c-2.75,2.749-7.208,2.749-9.957,0
+
c-2.75-2.754-2.75-7.21,0-9.958c0.68-0.68,1.467-1.193,2.307-1.538V36.369c-0.84-0.344-1.625-0.853-2.307-1.537
+
c-2.083-2.082-2.584-5.14-1.516-7.698L31.798,16.715L4.288,44.222c-2.311,2.313-2.311,6.06,0,8.371l40.121,40.118
+
c2.31,2.311,6.056,2.311,8.369,0L92.71,52.779C95.02,50.468,95.02,46.719,92.71,44.408z"/>
+</g>
+<g transform="matrix(1.1836353,0,0,1.1836353,57.486934,57.295456)">
+ <polygon fill="#0E3A5A" points="-0.001,12.4 32,12.4 32,19.6 -0.001,19.6
"/>
+</g>
+</svg>
diff --git a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/hop-gui-git.adoc
b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/hop-gui-git.adoc
index 1c23bdf3c8..f7734e8255 100644
--- a/docs/hop-user-manual/modules/ROOT/pages/hop-gui/hop-gui-git.adoc
+++ b/docs/hop-user-manual/modules/ROOT/pages/hop-gui/hop-gui-git.adoc
@@ -97,6 +97,30 @@ A transform or action that was renamed is marked as changed,
rather than as one
The hops around it are left alone.
This is recognised as long as the rename is all that happened to it, a move
aside: renaming and editing in the same commit still shows up as a removal plus
an addition, because there is nothing left to recognise it by.
+== Git Commit Perspective
+
+The git commit perspective (`CTRL+SHIFT+O`, `CMD+SHIFT+O` on macOS) lists all
files with changes in two groups: 'Staged' (added to git's index with 'Add')
and 'Unstaged' (everything else, including files git doesn't track yet).
+
+The toolbar and right-click options act on the files whose checkbox is ticked,
not on the highlighted row.
+Files git already knows about are ticked for you, so a commit takes your work
in progress along: untick a file (or unstage it) to leave it out.
+Files that are not tracked yet are listed but never ticked: committing a file
for the first time is something you decide, tick it yourself to include it.
+
+[options="header", cols="1,2,5,1"]
+|===
+|icon|action|description|shortcut
+|image:icons/refresh.svg[width="24px"]|Refresh|Reload the list of staged and
unstaged files.|F5
+|image:icons/git-add.svg[width="24px"]|Add|Stage the checked files. They move
to 'Staged' and become part of the next commit.|CTRL+ALT+A
+|image:icons/git-unstage.svg[width="24px"]|Unstage|Take the checked files out
of the next commit. They move back to 'Unstaged', your changes are
kept.|CTRL+ALT+U
+|image:icons/git-restore.svg[width="24px"]|Restore|Throw the changes in the
checked files away and restore the last committed version (HEAD). Asks for
confirmation.|CTRL+ALT+Z
+|image:icons/delete.svg[width="24px"]|Delete|Delete the checked files from
disk.|DEL
+|===
+
+WARNING: Unstage and Restore both act on staged files, but they do very
different things.
+Unstage only takes a file out of the next commit and keeps your work, Restore
throws your changes away.
+That is why Restore asks for confirmation and Unstage does not: nothing is
lost by unstaging, you can stage the file again with 'Add'.
+
+The same options are available in the right-click menu of the file list.
+
== Git Configuration options
There are a few global configuration options available for the git GUI plugin.
diff --git
a/plugins/misc/git/src/main/java/org/apache/hop/git/GitCommitPerspective.java
b/plugins/misc/git/src/main/java/org/apache/hop/git/GitCommitPerspective.java
index 9a75636b3f..928dfd5530 100644
---
a/plugins/misc/git/src/main/java/org/apache/hop/git/GitCommitPerspective.java
+++
b/plugins/misc/git/src/main/java/org/apache/hop/git/GitCommitPerspective.java
@@ -19,8 +19,10 @@ package org.apache.hop.git;
import java.io.File;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import lombok.Getter;
import org.apache.commons.vfs2.FileObject;
import org.apache.hop.core.Props;
@@ -96,6 +98,8 @@ public class GitCommitPerspective implements IHopPerspective {
public static final String CONTEXT_MENU_ADD =
"GitCommitPerspective-ContextMenu-10100-AddToGit";
public static final String CONTEXT_MENU_ADD_TO_GIT_IGNORE =
"GitCommitPerspective-ContextMenu-10110-AddToGitIgnore";
+ public static final String CONTEXT_MENU_UNSTAGE =
+ "GitCommitPerspective-ContextMenu-10120-Unstage";
public static final String CONTEXT_MENU_SHOW_TEXT_DIFF =
"GitCommitPerspective-ContextMenu-10200-ShowTextDiff";
public static final String CONTEXT_MENU_SHOW_GRAPH_DIFF =
@@ -107,6 +111,7 @@ public class GitCommitPerspective implements
IHopPerspective {
public static final String GUI_PLUGIN_TOOLBAR_PARENT_ID =
"GitCommitPerspective-Toolbar";
public static final String TOOLBAR_ITEM_REFRESH =
"GitCommitPerspective-Toolbar-10100-Refresh";
public static final String TOOLBAR_ITEM_ADD =
"GitCommitPerspective-Toolbar-10200-Add";
+ public static final String TOOLBAR_ITEM_UNSTAGE =
"GitCommitPerspective-Toolbar-10250-Unstage";
public static final String TOOLBAR_ITEM_RESTORE =
"GitCommitPerspective-Toolbar-10300-Restore";
public static final String TOOLBAR_ITEM_DELETE =
"GitCommitPerspective-Toolbar-10400-Delete";
@@ -254,6 +259,8 @@ public class GitCommitPerspective implements
IHopPerspective {
List<UIFile> selectedFiles = this.getSelectedFiles();
setMenuItemEnabled(menuWidgets, CONTEXT_MENU_ADD,
!getSelectedUntrackedFiles().isEmpty());
+ setMenuItemEnabled(
+ menuWidgets, CONTEXT_MENU_UNSTAGE,
!getSelectedStagedFiles().isEmpty());
setMenuItemEnabled(menuWidgets, CONTEXT_MENU_ADD_TO_GIT_IGNORE,
!selectedFiles.isEmpty());
setMenuItemEnabled(menuWidgets, CONTEXT_MENU_SHOW_TEXT_DIFF,
!selectedFiles.isEmpty());
setMenuItemEnabled(
@@ -328,33 +335,54 @@ public class GitCommitPerspective implements
IHopPerspective {
@GuiKeyboardShortcut(control = true, key = 'k', global = true)
@GuiOsxKeyboardShortcut(command = true, key = 'k', global = true)
- public void selectAllStaged() {
- checkStagedFiles();
+ public void selectAllChanged() {
+ checkChangedFiles();
wMessage.selectAll();
wMessage.setFocus();
}
/**
- * Check the staged files and uncheck the unstaged ones. Staged files are
the ones that go into
- * the next commit, so they are what the commit buttons act on by default.
+ * Check the files git already knows about, staged or not: the commit acts
on what is checked, so
+ * this offers all the work in progress for the next commit. Untracked files
are left unchecked:
+ * committing a file for the first time is a deliberate act, git doesn't do
it for you either.
*/
- private void checkStagedFiles() {
+ private void checkChangedFiles() {
if (wTree == null || wTree.isDisposed() || wTree.getItemCount() < 2) {
return;
}
- TreeItem stagedRootItem = this.getStagedRootItem();
- stagedRootItem.setChecked(stagedRootItem.getItemCount() > 0);
- for (TreeItem item : stagedRootItem.getItems()) {
- item.setChecked(true);
+ for (TreeItem rootItem : wTree.getItems()) {
+ boolean allChecked = rootItem.getItemCount() > 0;
+ for (TreeItem item : rootItem.getItems()) {
+ boolean tracked = !(item.getData() instanceof UIFile file) ||
!isUntracked(file);
+ item.setChecked(tracked);
+ allChecked = allChecked && tracked;
+ }
+ rootItem.setChecked(allChecked);
+ }
+ }
+
+ /** A file git doesn't know about yet: added in the working tree, not in the
index. */
+ private boolean isUntracked(UIFile file) {
+ return !file.isStaged() && file.getChangeType() ==
DiffEntry.ChangeType.ADD;
+ }
+
+ /** Uncheck the given files under the unstaged node, they are no longer part
of the commit. */
+ private void uncheckUnstagedFiles(Set<String> fileNames) {
+ if (wTree == null || wTree.isDisposed() || wTree.getItemCount() < 2 ||
fileNames.isEmpty()) {
+ return;
}
TreeItem unstagedRootItem = this.getUnstagedRootItem();
- unstagedRootItem.setChecked(false);
+ boolean allChecked = true;
for (TreeItem item : unstagedRootItem.getItems()) {
- item.setChecked(false);
+ if (item.getData() instanceof UIFile file &&
fileNames.contains(file.getName())) {
+ item.setChecked(false);
+ }
+ allChecked = allChecked && item.getChecked();
}
+ unstagedRootItem.setChecked(allChecked && unstagedRootItem.getItemCount()
> 0);
}
protected void retrieveState() {
@@ -462,6 +490,58 @@ public class GitCommitPerspective implements
IHopPerspective {
}
}
+ /**
+ * Unstage the selected staged files: the equivalent of git restore
--staged. The changes are kept
+ * in the working tree, only the index is updated. Use {@link
#restoreFiles()} to throw the
+ * changes away.
+ */
+ @GuiMenuElement(
+ root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+ parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
+ id = CONTEXT_MENU_UNSTAGE,
+ label = "i18n::GitCommitPerspective.Menu.Unstage.Text",
+ image = "git-unstage.svg")
+ @GuiToolbarElement(
+ root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+ id = TOOLBAR_ITEM_UNSTAGE,
+ toolTip = "i18n::GitCommitPerspective.Toolbar.Unstage.Tooltip",
+ image = "git-unstage.svg")
+ @GuiKeyboardShortcut(control = true, alt = true, key = 'U')
+ @GuiOsxKeyboardShortcut(control = true, alt = true, key = 'U')
+ public void unstageFiles() {
+ try {
+ List<UIFile> files = getSelectedStagedFiles();
+ if (files.isEmpty()) {
+ return;
+ }
+
+ UIGit git = GitGuiPlugin.getInstance().getGit();
+ Set<String> unstagedFileNames = new HashSet<>();
+ for (UIFile file : files) {
+ git.resetPath(file.getName());
+ unstagedFileNames.add(file.getName());
+ }
+
+ GitGuiPlugin.getInstance().beforeRefresh();
+ refresh();
+
+ // Taking a file out of the commit is the point of unstaging it, so
leave these unchecked
+ // even though refresh() offers every changed file by default.
+ //
+ uncheckUnstagedFiles(unstagedFileNames);
+ updateGui();
+
+ // Refresh the tree, change colors...
+ ExplorerPerspective.getInstance().refresh();
+ } catch (Exception e) {
+ new ErrorDialog(
+ getShell(),
+ BaseMessages.getString(PKG,
"GitGuiPlugin.Dialog.UnstageError.Header"),
+ BaseMessages.getString(PKG,
"GitGuiPlugin.Dialog.UnstageError.Message"),
+ e);
+ }
+ }
+
@GuiMenuElement(
root = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
parentId = GUI_PLUGIN_CONTEXT_MENU_PARENT_ID,
@@ -556,6 +636,8 @@ public class GitCommitPerspective implements
IHopPerspective {
@GuiToolbarElement(
root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
id = TOOLBAR_ITEM_RESTORE,
+ // Keep restore, which throws changes away, out of the staging group
+ separator = true,
toolTip = "i18n::GitCommitPerspective.Toolbar.Restore.Tooltip",
image = "git-restore.svg")
@GuiKeyboardShortcut(alt = true, control = true, key = 'Z')
@@ -900,6 +982,7 @@ public class GitCommitPerspective implements
IHopPerspective {
wMessage.setEnabled(gitEnabled);
toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_ADD,
!getSelectedUntrackedFiles().isEmpty());
+ toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_UNSTAGE,
!getSelectedStagedFiles().isEmpty());
toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_RESTORE,
!getSelectedStagedFiles().isEmpty());
toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_DELETE,
!getSelectedFiles().isEmpty());
@@ -941,30 +1024,21 @@ public class GitCommitPerspective implements
IHopPerspective {
GitResource resource = GitResource.getInstance();
for (String fileName : filesToCommit.keySet()) {
UIFile file = filesToCommit.get(fileName);
- TreeItem item = null;
+
+ // What is in the index goes under "Staged", the rest under
"Unstaged", whatever the
+ // type of change is. A file that gets unstaged has to move to the
unstaged node.
+ //
+ TreeItem item = new TreeItem(file.isStaged() ? stagedItem :
untrackedItem, SWT.NONE);
switch (file.getChangeType()) {
- case ADD, COPY, RENAME:
- {
- item = new TreeItem(file.isStaged() ? stagedItem :
untrackedItem, SWT.NONE);
+ case ADD, COPY, RENAME ->
item.setForeground(
file.isStaged() ? resource.getStagedAddColor() :
resource.getUnstagedColor());
- break;
- }
- case MODIFY:
- {
- item = new TreeItem(stagedItem, SWT.NONE);
+ case MODIFY ->
item.setForeground(
file.isStaged()
? resource.getStagedModifyColor()
: resource.getUnstagedColor());
- break;
- }
- case DELETE:
- {
- item = new TreeItem(stagedItem, SWT.NONE);
- item.setForeground(resource.getIgnoredColor());
- break;
- }
+ case DELETE -> item.setForeground(resource.getIgnoredColor());
}
item.setText(file.getName());
@@ -975,9 +1049,9 @@ public class GitCommitPerspective implements
IHopPerspective {
stagedItem.setExpanded(true);
untrackedItem.setExpanded(true);
- // Start from the staged files, the way a "git commit" would
+ // Offer all the work in progress for the next commit
//
- checkStagedFiles();
+ checkChangedFiles();
}
wTree.setRedraw(true);
diff --git a/plugins/misc/git/src/main/resources/git-unstage.svg
b/plugins/misc/git/src/main/resources/git-unstage.svg
new file mode 100644
index 0000000000..ffc918179d
--- /dev/null
+++ b/plugins/misc/git/src/main/resources/git-unstage.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!--
+ - 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.
+ -->
+
+<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="97px" height="97px" viewBox="0 0 97 97" enable-background="new
0 0 97 97" xml:space="preserve">
+<g transform="matrix(0.84298576,0,0,0.84298576,0.40113214,0.40160318)">
+ <path fill="#F05133"
d="M92.71,44.408L52.591,4.291c-2.31-2.311-6.057-2.311-8.369,0l-8.33,8.332L46.459,23.19
+
c2.456-0.829,5.272-0.273,7.229,1.686c1.969,1.97,2.521,4.81,1.67,7.275l10.187,10.185c2.465-0.85,5.306-0.3,7.275,1.671
+
c2.749,2.75,2.749,7.206,0,9.958c-2.752,2.751-7.208,2.751-9.962,0c-2.068-2.07-2.58-5.11-1.53-7.658l-9.501-9.499v24.998
+
c0.67,0.331,1.303,0.774,1.861,1.332c2.75,2.749,2.75,7.206,0,9.958c-2.75,2.749-7.208,2.749-9.957,0
+
c-2.75-2.754-2.75-7.21,0-9.958c0.68-0.68,1.467-1.193,2.307-1.538V36.369c-0.84-0.344-1.625-0.853-2.307-1.537
+
c-2.083-2.082-2.584-5.14-1.516-7.698L31.798,16.715L4.288,44.222c-2.311,2.313-2.311,6.06,0,8.371l40.121,40.118
+
c2.31,2.311,6.056,2.311,8.369,0L92.71,52.779C95.02,50.468,95.02,46.719,92.71,44.408z"/>
+</g>
+<g transform="matrix(1.1836353,0,0,1.1836353,57.486934,57.295456)">
+ <polygon fill="#0E3A5A" points="-0.001,12.4 32,12.4 32,19.6 -0.001,19.6
"/>
+</g>
+</svg>
diff --git
a/plugins/misc/git/src/main/resources/org/apache/hop/git/messages/messages_en_US.properties
b/plugins/misc/git/src/main/resources/org/apache/hop/git/messages/messages_en_US.properties
index ddebb027d4..e957e2b51f 100644
---
a/plugins/misc/git/src/main/resources/org/apache/hop/git/messages/messages_en_US.properties
+++
b/plugins/misc/git/src/main/resources/org/apache/hop/git/messages/messages_en_US.properties
@@ -80,6 +80,8 @@ GitGuiPlugin.Dialog.RevertCommitConfirmation.Message=Are you
sure you want to re
GitGuiPlugin.Dialog.RevertCommitRepositoryIsNotClean.Message=Please clean your
repository working tree before reverting commits.
GitGuiPlugin.Dialog.RevertFiles.Header=Select files to revert
GitGuiPlugin.Dialog.RevertFiles.Message=Please select the files to revert
+GitGuiPlugin.Dialog.UnstageError.Header=GIT Unstage Error
+GitGuiPlugin.Dialog.UnstageError.Message=There was an error unstaging the
selected files
GitGuiPlugin.Dialog.Tag.CreateTag.Header=Create a Tag
GitGuiPlugin.Dialog.Tag.CreateTag.Message=Enter the name of the new tag
GitGuiPlugin.Dialog.Tag.DeleteTagConfirmation.Header=Delete a Tag
@@ -126,6 +128,7 @@ GitCommitPerspective.Button.Restore.Label=Restore
GitCommitPerspective.Menu.AddToGit.Text=Add
GitCommitPerspective.Menu.AddToGitIgnore.Text=Add to .gitignore
GitCommitPerspective.Menu.Restore.Text=Restore...
+GitCommitPerspective.Menu.Unstage.Text=Unstage
GitCommitPerspective.Menu.ShowTextDiff.Text=Show text diff
GitCommitPerspective.Menu.ShowGraphDiff.Text=Show visual diff
GitCommitPerspective.Menu.Delete.Text=Delete...
@@ -135,10 +138,11 @@
GitCommitPerspective.Status.CommittedAndPushed.Message=Committed and pushed {0}
GitCommitPerspective.Status.CommitId.Label=as {0}
GitCommitPerspective.Status.Staged.Label=Staged
GitCommitPerspective.Status.Unstaged.Label=Unstaged
-GitCommitPerspective.Toolbar.AddToGit.Tooltip=Add
+GitCommitPerspective.Toolbar.AddToGit.Tooltip=Stage the selected files
GitCommitPerspective.Toolbar.Delete.Tooltip=Delete...
GitCommitPerspective.Toolbar.Refresh.Tooltip=Refresh
-GitCommitPerspective.Toolbar.Restore.Tooltip=Restore file to the last
committed version (HEAD)...
+GitCommitPerspective.Toolbar.Restore.Tooltip=Discard changes, restore the last
committed version (HEAD)...
+GitCommitPerspective.Toolbar.Unstage.Tooltip=Unstage the selected files, the
changes are kept
GitPerspective.Description=Git perspective (branches, tags, commit history)
GitPerspective.Name=Git
diff --git
a/plugins/misc/git/src/test/java/org/apache/hop/git/model/UIGitTest.java
b/plugins/misc/git/src/test/java/org/apache/hop/git/model/UIGitTest.java
index 670baccdba..b500bb1e57 100644
--- a/plugins/misc/git/src/test/java/org/apache/hop/git/model/UIGitTest.java
+++ b/plugins/misc/git/src/test/java/org/apache/hop/git/model/UIGitTest.java
@@ -652,6 +652,60 @@ public class UIGitTest extends RepositoryTestCase {
assertFalse(tags.contains("test"));
}
+ @Test
+ public void testUnstageFilesWithResetPath() throws Exception {
+ initialCommit();
+
+ // Stage a change to a tracked file and a brand new file
+ writeTrashFile("Test.txt", "Changed");
+ uiGit.add("Test.txt");
+ writeTrashFile("New.txt", "New file");
+ uiGit.add("New.txt");
+ assertEquals(2, uiGit.getStagedFiles().size());
+
+ for (UIFile file : uiGit.getStagedFiles()) {
+ uiGit.resetPath(file.getName());
+ }
+
+ // Nothing staged anymore, but both changes are still there
+ assertTrue(uiGit.getStagedFiles().isEmpty());
+ assertEquals(2, uiGit.getUnstagedFiles().size());
+ assertEquals("Changed", read(new File(db.getWorkTree(), "Test.txt")));
+ }
+
+ @Test
+ public void testUntrackedAndModifiedFileFlags() throws Exception {
+ initialCommit();
+
+ // A file git doesn't know about yet and a change to a file it does know
about
+ writeTrashFile("New.txt", "New file");
+ writeTrashFile("Test.txt", "Changed");
+
+ List<UIFile> unstagedFiles = uiGit.getUnstagedFiles();
+ UIFile untracked = findFile(unstagedFiles, "New.txt");
+ UIFile modified = findFile(unstagedFiles, "Test.txt");
+
+ // The commit perspective tells both apart by change type to decide what
it offers for the
+ // next commit: an unstaged ADD is a file git doesn't track yet, which is
never checked for you
+ assertEquals(ChangeType.ADD, untracked.getChangeType());
+ assertFalse(untracked.isStaged());
+ assertEquals(ChangeType.MODIFY, modified.getChangeType());
+ assertFalse(modified.isStaged());
+
+ // Once added, the same new file is staged
+ uiGit.add("New.txt");
+ UIFile staged = findFile(uiGit.getStagedFiles(), "New.txt");
+ assertEquals(ChangeType.ADD, staged.getChangeType());
+ assertTrue(staged.isStaged());
+ }
+
+ private UIFile findFile(List<UIFile> files, String name) {
+ return files.stream()
+ .filter(file -> file.getName().equals(name))
+ .findFirst()
+ .orElseThrow(() -> new AssertionError("File '" + name + "' not
found"));
+ }
+
@Test
public void testCloneShouldFail() throws Exception {
// WhenDirAlreadyExists