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 87ac0ff46c improved messages/dialogs when merging branches. fixes
#7691 (#7697)
87ac0ff46c is described below
commit 87ac0ff46c21e06b06ab868e5dc6e625261b62b3
Author: Bart Maertens <[email protected]>
AuthorDate: Fri Jul 31 09:03:42 2026 +0200
improved messages/dialogs when merging branches. fixes #7691 (#7697)
---
.../org/apache/hop/git/GitCommitPerspective.java | 111 ++++++++++++----
.../main/java/org/apache/hop/git/GitGuiPlugin.java | 10 +-
.../main/java/org/apache/hop/git/model/UIGit.java | 147 +++++++++++++++++++--
.../hop/git/messages/messages_en_US.properties | 4 +
.../git/model/messages/messages_en_US.properties | 9 ++
.../java/org/apache/hop/git/model/UIGitTest.java | 97 ++++++++++++++
6 files changed, 339 insertions(+), 39 deletions(-)
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 b919e537cf..82c1f2ff2b 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
@@ -67,6 +67,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
@@ -117,7 +118,7 @@ public class GitCommitPerspective implements
IHopPerspective {
private Control wToolBar;
private Tree wTree;
private Text wMessage;
- private CLabel wError;
+ private CLabel wStatus;
private Button wAmend;
private Button wCommit;
private Button wCommitAndPush;
@@ -297,16 +298,15 @@ public class GitCommitPerspective implements
IHopPerspective {
wCommitAndPush.addListener(SWT.Selection, event -> commitFiles(true));
PropsUi.setLook(wCommitAndPush);
- wError = new CLabel(composite, SWT.NONE);
- wError.setImage(GuiResource.getInstance().getImageError());
- wError.setLayoutData(
+ wStatus = new CLabel(composite, SWT.NONE);
+ wStatus.setLayoutData(
FormDataBuilder.builder()
.bottom()
.left(wCommitAndPush, ConstUi.MEDIUM_MARGIN)
.right()
.result());
- wError.setVisible(false);
- PropsUi.setLook(wError);
+ wStatus.setVisible(false);
+ PropsUi.setLook(wStatus);
wMessage = new Text(composite, SWT.BORDER | SWT.MULTI);
wMessage.setLayoutData(
@@ -329,24 +329,32 @@ public class GitCommitPerspective implements
IHopPerspective {
@GuiKeyboardShortcut(control = true, key = 'k', global = true)
@GuiOsxKeyboardShortcut(command = true, key = 'k', global = true)
public void selectAllStaged() {
+ checkStagedFiles();
+
+ 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.
+ */
+ private void checkStagedFiles() {
+ if (wTree == null || wTree.isDisposed() || wTree.getItemCount() < 2) {
+ return;
+ }
+
TreeItem stagedRootItem = this.getStagedRootItem();
- if (stagedRootItem != null) {
- stagedRootItem.setChecked(true);
- for (TreeItem item : stagedRootItem.getItems()) {
- item.setChecked(true);
- }
+ stagedRootItem.setChecked(stagedRootItem.getItemCount() > 0);
+ for (TreeItem item : stagedRootItem.getItems()) {
+ item.setChecked(true);
}
TreeItem unstagedRootItem = this.getUnstagedRootItem();
- if (unstagedRootItem != null) {
- unstagedRootItem.setChecked(false);
- for (TreeItem item : unstagedRootItem.getItems()) {
- item.setChecked(false);
- }
+ unstagedRootItem.setChecked(false);
+ for (TreeItem item : unstagedRootItem.getItems()) {
+ item.setChecked(false);
}
-
- wMessage.selectAll();
- wMessage.setFocus();
}
protected void retrieveState() {
@@ -710,10 +718,18 @@ public class GitCommitPerspective implements
IHopPerspective {
List<UIFile> filesToIgnore =
this.getSelectedFiles(getStagedRootItem().getItems(), new
ArrayList<>(), false);
- // No files to commit
+ // No files to commit. Selecting a row in the tree only highlights it,
so tell the user to
+ // check the box when there are files listed but none of them are
checked.
+ //
if (filesToCommit.isEmpty()) {
- wError.setText(BaseMessages.getString(PKG,
"GitGuiPlugin.Dialog.NoFilesToCommit.Message"));
- wError.setVisible(true);
+ boolean treeHasFiles = !filesToIgnore.isEmpty() ||
!getUncheckedFiles().isEmpty();
+ showStatus(
+ GuiResource.getInstance().getImageError(),
+ BaseMessages.getString(
+ PKG,
+ treeHasFiles
+ ? "GitCommitPerspective.Error.NoFilesChecked.Message"
+ : "GitGuiPlugin.Dialog.NoFilesToCommit.Message"));
return;
}
@@ -737,6 +753,7 @@ public class GitCommitPerspective implements
IHopPerspective {
// Commit selected files
uiGit.commit(authorName, message, amend);
+ String commitId = uiGit.getCommitId(Constants.HEAD);
// Restore unselected staged files
if (!filesToIgnore.isEmpty()) {
@@ -755,9 +772,10 @@ public class GitCommitPerspective implements
IHopPerspective {
wAmend.setSelection(false);
+ boolean pushed = false;
if (push) {
try {
- uiGit.push();
+ pushed = uiGit.push();
} catch (Exception e) {
new ErrorDialog(
HopGui.getInstance().getShell(),
@@ -766,6 +784,19 @@ public class GitCommitPerspective implements
IHopPerspective {
e);
}
}
+
+ // Confirm what happened. The status stays until the next thing the user
does, no dialog to
+ // click away. It goes last: refreshing the tree clears the status line.
+ //
+ showStatus(
+ GuiResource.getInstance().getImageSuccess(),
+ BaseMessages.getString(
+ PKG,
+ pushed
+ ? "GitCommitPerspective.Status.CommittedAndPushed.Message"
+ : "GitCommitPerspective.Status.Committed.Message",
+ Integer.toString(filesToCommit.size()),
+ getCommitIdLabel(commitId)));
} catch (Exception e) {
new ErrorDialog(
HopGui.getInstance().getShell(),
@@ -775,6 +806,27 @@ public class GitCommitPerspective implements
IHopPerspective {
}
}
+ /**
+ * Show a line of feedback next to the commit buttons. It is wiped by the
next {@link
+ * #updateGui()} so it lives exactly as long as the user's next action.
+ */
+ private void showStatus(Image image, String message) {
+ wStatus.setImage(image);
+ wStatus.setText(message);
+ wStatus.setVisible(true);
+ }
+
+ /**
+ * The short commit id to append to the status, or nothing at all if git did
not hand one back.
+ */
+ private String getCommitIdLabel(String commitId) {
+ if (commitId == null || commitId.length() < 7) {
+ return "";
+ }
+ return BaseMessages.getString(
+ PKG, "GitCommitPerspective.Status.CommitId.Label",
commitId.substring(0, 7));
+ }
+
private TreeItem getStagedRootItem() {
return wTree.getItems()[0];
}
@@ -810,6 +862,13 @@ public class GitCommitPerspective implements
IHopPerspective {
return getSelectedFiles(getUnstagedRootItem().getItems(), new
ArrayList<>(), true);
}
+ protected List<UIFile> getUncheckedFiles() {
+ if (wTree == null || wTree.isDisposed()) {
+ return List.of();
+ }
+ return getSelectedFiles(wTree.getItems(), new ArrayList<>(), false);
+ }
+
protected List<UIFile> getSelectedFiles(TreeItem[] items, List<UIFile>
files, boolean selected) {
for (TreeItem item : items) {
if ((item.getData() instanceof UIFile file) && item.getChecked() ==
selected) {
@@ -833,7 +892,7 @@ public class GitCommitPerspective implements
IHopPerspective {
toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_RESTORE,
!getSelectedStagedFiles().isEmpty());
toolBarWidgets.enableToolbarItem(TOOLBAR_ITEM_DELETE,
!getSelectedFiles().isEmpty());
- wError.setVisible(false);
+ wStatus.setVisible(false);
wCommit.setEnabled(commitEnabled);
wCommitAndPush.setEnabled(commitEnabled);
}
@@ -904,6 +963,10 @@ public class GitCommitPerspective implements
IHopPerspective {
stagedItem.setExpanded(true);
untrackedItem.setExpanded(true);
+
+ // Start from the staged files, the way a "git commit" would
+ //
+ checkStagedFiles();
}
wTree.setRedraw(true);
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 f89aa9099e..bf47c3a671 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
@@ -292,10 +292,14 @@ public class GitGuiPlugin
image = "pull.svg")
public void gitPull() {
try {
- if (git.pull()) {
- // Refresh the explorer file, refs and commit history
- GitPerspective.getInstance().refresh(true);
+ boolean merged = git.pull();
+ // Refresh the explorer file, refs and commit history. A pull fetches
the remote refs even
+ // when there was nothing to merge into the current branch.
+ //
+ GitPerspective.getInstance().refresh(true);
+
+ if (merged) {
MessageBox pullSuccessful =
new MessageBox(HopGui.getInstance().getShell(),
SWT.ICON_INFORMATION);
pullSuccessful.setText(
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 eb79851d5c..8944fcb759 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
@@ -29,12 +29,14 @@ import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
import lombok.Getter;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
@@ -64,6 +66,7 @@ import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.RevertCommand;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.StatusCommand;
+import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.diff.DiffEntry;
@@ -123,6 +126,9 @@ public class UIGit extends VCS {
"Authentication is required but no CredentialsProvider has been
registered";
public static final String CONST_NOT_AUTHORIZED = "not authorized";
public static final String CONST_DIALOG_ERROR = "Dialog.Error";
+ public static final String CONST_MERGE_FAILED_HEADER =
"UIGit.Dialog.MergeFailed.Header";
+ public static final String CONST_MERGE_FAILED_LOCAL_CHANGES =
+ "UIGit.Dialog.MergeFailed.LocalChanges.Message";
static {
/**
@@ -1077,22 +1083,52 @@ public class UIGit extends VCS {
try {
ObjectId obj = git.getRepository().resolve(value);
MergeResult result =
git.merge().include(obj).setStrategy(mergeStrategy).call();
- if (result.getMergeStatus().isSuccessful()) {
- return true;
- } else {
- // TODO: get rid of message box
+ if (result.getMergeStatus() == MergeStatus.ALREADY_UP_TO_DATE) {
+ // The merge succeeded but did nothing. That is surprising to anyone
with work in progress,
+ // so point out that staged or modified files are not part of a branch
yet.
//
showMessageBox(
- BaseMessages.getString(PKG, CONST_DIALOG_ERROR),
result.getMergeStatus().toString());
- if (result.getMergeStatus() == MergeStatus.CONFLICTING) {
- Map<String, int[][]> conflicts = result.getConflicts();
- for (String path : conflicts.keySet()) {
- checkout(path, Constants.HEAD, CONST_OURS);
- checkout(path, getExpandedName(value, VCS.TYPE_BRANCH),
CONST_THEIRS);
- }
- return true;
+ BaseMessages.getString(PKG, "UIGit.Dialog.NothingToMerge.Header"),
+ BaseMessages.getString(
+ PKG,
+ "UIGit.Dialog.NothingToMerge.Message",
+ value,
+ getBranch(),
+ getUncommittedChangesBlock()));
+ return false;
+ }
+ if (result.getMergeStatus().isSuccessful()) {
+ return true;
+ }
+
+ boolean conflicting = result.getMergeStatus() == MergeStatus.CONFLICTING;
+ if (conflicting) {
+ Map<String, int[][]> conflicts = result.getConflicts();
+ for (String path : conflicts.keySet()) {
+ checkout(path, Constants.HEAD, CONST_OURS);
+ checkout(path, getExpandedName(value, VCS.TYPE_BRANCH),
CONST_THEIRS);
}
}
+
+ // TODO: get rid of message box
+ //
+ showMessageBox(
+ BaseMessages.getString(PKG, CONST_MERGE_FAILED_HEADER),
+ getMergeFailureMessage(value, result));
+
+ return conflicting;
+ } catch (CheckoutConflictException e) {
+ // A fast-forward merge never starts when local changes are in the way.
Explain that instead
+ // of showing a stack trace.
+ //
+ showMessageBox(
+ BaseMessages.getString(PKG, CONST_MERGE_FAILED_HEADER),
+ BaseMessages.getString(
+ PKG,
+ CONST_MERGE_FAILED_LOCAL_CHANGES,
+ value,
+ getBranch(),
+ getPathList(e.getConflictingPaths())));
return false;
} catch (Exception e) {
throw new HopException(
@@ -1100,6 +1136,93 @@ public class UIGit extends VCS {
}
}
+ /**
+ * JGit only reports a terse status like "Failed" or "Conflicting". Turn
that into an explanation
+ * the user can act on: which files are in the way and what to do about them.
+ *
+ * @param value the branch, tag or commit that was merged in
+ * @param result the unsuccessful merge result
+ * @return a message describing the failure and how to recover from it
+ */
+ private String getMergeFailureMessage(String value, MergeResult result) {
+ String currentBranch = getBranch();
+ switch (result.getMergeStatus()) {
+ case FAILED:
+ return getLocalChangesMessage(
+ value,
+ currentBranch,
+ result.getFailingPaths() == null ? null :
result.getFailingPaths().keySet(),
+ result);
+ case CHECKOUT_CONFLICT:
+ return getLocalChangesMessage(value, currentBranch,
result.getCheckoutConflicts(), result);
+ case CONFLICTING:
+ return BaseMessages.getString(
+ PKG,
+ "UIGit.Dialog.MergeFailed.Conflicting.Message",
+ value,
+ currentBranch,
+ getPathList(result.getConflicts() == null ? Set.of() :
result.getConflicts().keySet()));
+ case ABORTED:
+ return BaseMessages.getString(
+ PKG, "UIGit.Dialog.MergeFailed.Aborted.Message", value,
currentBranch);
+ default:
+ return getGenericMessage(value, currentBranch, result);
+ }
+ }
+
+ /**
+ * Local changes are in the way. Naming them is the whole point of the
message, so fall back on
+ * the plain status when JGit did not tell us which files those are.
+ */
+ private String getLocalChangesMessage(
+ String value, String currentBranch, Collection<String> paths,
MergeResult result) {
+ if (paths == null || paths.isEmpty()) {
+ return getGenericMessage(value, currentBranch, result);
+ }
+ return BaseMessages.getString(
+ PKG, CONST_MERGE_FAILED_LOCAL_CHANGES, value, currentBranch,
getPathList(paths));
+ }
+
+ private String getGenericMessage(String value, String currentBranch,
MergeResult result) {
+ return BaseMessages.getString(
+ PKG,
+ "UIGit.Dialog.MergeFailed.Generic.Message",
+ value,
+ currentBranch,
+ result.getMergeStatus().toString());
+ }
+
+ /**
+ * Nothing to merge usually means the work is still sitting in the index or
the working tree
+ * instead of on the branch. List those files so the user can see where
their changes are.
+ *
+ * @return the uncommitted changes to mention, or an empty string when the
working tree is clean
+ */
+ private String getUncommittedChangesBlock() {
+ try {
+ Status status = git.status().call();
+ if (!status.hasUncommittedChanges()) {
+ return "";
+ }
+ Set<String> paths = new TreeSet<>();
+ paths.addAll(status.getAdded());
+ paths.addAll(status.getChanged());
+ paths.addAll(status.getRemoved());
+ paths.addAll(status.getModified());
+ paths.addAll(status.getMissing());
+ return BaseMessages.getString(
+ PKG, "UIGit.Dialog.NothingToMerge.UncommittedChanges.Label",
getPathList(paths));
+ } catch (NoWorkTreeException | GitAPIException e) {
+ return "";
+ }
+ }
+
+ private String getPathList(Collection<String> paths) {
+ StringBuilder list = new StringBuilder();
+ paths.forEach(path -> list.append(" - ").append(path).append(Const.CR));
+ return list.toString();
+ }
+
private boolean hasUncommittedChanges() {
try {
return git.status().call().hasUncommittedChanges();
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 83b36c1b19..aa456a7190 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
@@ -121,6 +121,10 @@ GitCommitPerspective.Menu.Restore.Text=Restore...
GitCommitPerspective.Menu.ShowTextDiff.Text=Show text diff
GitCommitPerspective.Menu.ShowGraphDiff.Text=Show visual diff
GitCommitPerspective.Menu.Delete.Text=Delete...
+GitCommitPerspective.Error.NoFilesChecked.Message=Please check the box in
front of the files you want to commit
+GitCommitPerspective.Status.Committed.Message=Committed {0} file(s) {1}
+GitCommitPerspective.Status.CommittedAndPushed.Message=Committed and pushed
{0} file(s) {1}
+GitCommitPerspective.Status.CommitId.Label=as {0}
GitCommitPerspective.Status.Staged.Label=Staged
GitCommitPerspective.Status.Unstaged.Label=Unstaged
GitCommitPerspective.Toolbar.AddToGit.Tooltip=Add
diff --git
a/plugins/misc/git/src/main/resources/org/apache/hop/git/model/messages/messages_en_US.properties
b/plugins/misc/git/src/main/resources/org/apache/hop/git/model/messages/messages_en_US.properties
index 93ca93b4d5..2fe94624ea 100644
---
a/plugins/misc/git/src/main/resources/org/apache/hop/git/model/messages/messages_en_US.properties
+++
b/plugins/misc/git/src/main/resources/org/apache/hop/git/model/messages/messages_en_US.properties
@@ -19,3 +19,12 @@
Dialog.Error=Error
Dialog.Success=Success
Git.Dialog.UncommittedChanges.Message=There are uncommitted changes
+
+UIGit.Dialog.MergeFailed.Header=Merge failed
+UIGit.Dialog.MergeFailed.LocalChanges.Message=Your local changes to the
following files would be overwritten by merging ''{0}'' into
''{1}'':\n{2}\nPlease commit or revert your changes before you merge.
+UIGit.Dialog.MergeFailed.Conflicting.Message=Merging ''{0}'' into ''{1}'' ran
into conflicts:\n{2}\nA ''.ours'' and a ''.theirs'' copy was written next to
every conflicting file. Resolve the conflicts and commit the result.
+UIGit.Dialog.MergeFailed.Aborted.Message=The merge of ''{0}'' into ''{1}'' was
aborted. Your files were left untouched.
+UIGit.Dialog.MergeFailed.Generic.Message=''{0}'' was not merged into ''{1}''.
Git reported: {2}
+UIGit.Dialog.NothingToMerge.Header=Nothing to merge
+UIGit.Dialog.NothingToMerge.Message=''{1}'' is already up to date with
''{0}''.{2}
+UIGit.Dialog.NothingToMerge.UncommittedChanges.Label=\n\nYou have uncommitted
changes:\n{0}
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 89a1486176..dcbe60aab5 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
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import java.io.File;
import java.io.InputStream;
@@ -48,12 +49,14 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
public class UIGitTest extends RepositoryTestCase {
private Git git;
@@ -511,6 +514,100 @@ public class UIGitTest extends RepositoryTestCase {
assertFalse(file.exists());
}
+ @Test
+ public void testMergeBranchWithUncommittedChangesExplainsWhatToDo() throws
Exception {
+ initialCommit();
+ commitOnBranch("develop", "Test.txt", "Hello from develop");
+
+ // Let master move on as well, so merging develop is a real merge and not
a fast-forward
+ //
+ git.checkout().setName(Constants.MASTER).call();
+ writeTrashFile("Other.txt", "Hello master");
+ git.add().addFilepattern("Other.txt").call();
+ git.commit().setMessage("master commit").call();
+
+ // Leave an uncommitted change in the file the merge needs to touch
+ //
+ writeTrashFile("Test.txt", "Uncommitted work");
+
+ assertFalse(uiGit.mergeBranch("develop", MergeStrategy.RECURSIVE));
+ assertMergeFailureExplained();
+ }
+
+ @Test
+ public void testFastForwardMergeWithUncommittedChangesExplainsWhatToDo()
throws Exception {
+ initialCommit();
+ commitOnBranch("develop", "Test.txt", "Hello from develop");
+
+ // Master is untouched, so merging develop fast-forwards. The checkout of
Test.txt can't happen
+ // while it holds uncommitted changes.
+ //
+ git.checkout().setName(Constants.MASTER).call();
+ writeTrashFile("Test.txt", "Uncommitted work");
+
+ assertFalse(uiGit.mergeBranch("develop", MergeStrategy.RECURSIVE));
+ assertMergeFailureExplained();
+ }
+
+ @Test
+ public void
testMergeBranchWithStagedButUncommittedWorkIsNotReportedAsSuccess() throws
Exception {
+ initialCommit();
+
+ // Stage a file on the branch but never commit it, so the branch holds no
commits of its own
+ //
+ git.branchCreate().setName("test-branch").call();
+ git.checkout().setName("test-branch").call();
+ writeTrashFile("bogus-pipeline.hpl", "not committed");
+ git.add().addFilepattern("bogus-pipeline.hpl").call();
+
+ git.checkout().setName(Constants.MASTER).call();
+
+ // There is nothing to merge, so this is not a successful merge
+ //
+ assertFalse(uiGit.mergeBranch("test-branch", MergeStrategy.RECURSIVE));
+
+ ArgumentCaptor<String> message = ArgumentCaptor.forClass(String.class);
+ verify(uiGit).showMessageBox(anyString(), message.capture());
+
+ assertTrue(message.getValue(), message.getValue().contains("is already up
to date with"));
+ assertTrue(message.getValue(), message.getValue().contains("You have
uncommitted changes"));
+ assertTrue(message.getValue(),
message.getValue().contains("bogus-pipeline.hpl"));
+ }
+
+ @Test
+ public void
testMergeBranchUpToDateWithCleanWorkingTreeDoesNotMentionUncommittedChanges()
+ throws Exception {
+ initialCommit();
+ git.branchCreate().setName("test-branch").call();
+
+ assertFalse(uiGit.mergeBranch("test-branch", MergeStrategy.RECURSIVE));
+
+ ArgumentCaptor<String> message = ArgumentCaptor.forClass(String.class);
+ verify(uiGit).showMessageBox(anyString(), message.capture());
+
+ assertTrue(message.getValue(), message.getValue().contains("is already up
to date with"));
+ assertFalse(message.getValue().contains("You have uncommitted changes"));
+ }
+
+ /** The user needs to know which file is in the way and how to get past it.
*/
+ private void assertMergeFailureExplained() {
+ ArgumentCaptor<String> message = ArgumentCaptor.forClass(String.class);
+ verify(uiGit).showMessageBox(anyString(), message.capture());
+
+ assertTrue(message.getValue(), message.getValue().contains("Test.txt"));
+ assertTrue(
+ message.getValue(),
+ message.getValue().contains("Please commit or revert your changes
before you merge"));
+ }
+
+ private void commitOnBranch(String branch, String file, String content)
throws Exception {
+ git.branchCreate().setName(branch).call();
+ git.checkout().setName(branch).call();
+ writeTrashFile(file, content);
+ git.add().addFilepattern(file).call();
+ git.commit().setMessage(branch + " commit").call();
+ }
+
private RevCommit initialCommit() throws Exception {
writeTrashFile("Test.txt", "Hello world");
git.add().addFilepattern("Test.txt").call();