--- git/nbproject/project.xml
+++ git/nbproject/project.xml
@@ -25,7 +25,7 @@
1
- 1.25
+ 1.26
--- git/src/org/netbeans/modules/git/Git.java
+++ git/src/org/netbeans/modules/git/Git.java
@@ -59,6 +59,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.libs.git.GitException;
+import org.netbeans.libs.git.GitRepository;
import org.netbeans.modules.git.client.CredentialsCallback;
import org.netbeans.modules.git.client.GitClient;
import org.netbeans.modules.git.ui.shelve.ShelveChangesAction;
@@ -212,15 +213,14 @@
}
public GitClient getClient (File repository, GitProgressSupport progressSupport, boolean handleAuthenticationIssues) throws GitException {
- // get the only instance for the repository folder, so we can synchronize on it
- File repositoryFolder = getRepositoryRoot(repository);
- if (repositoryFolder != null && repository.equals(repositoryFolder)) {
- repository = repositoryFolder;
- }
- GitClient client = new GitClient(repository, progressSupport, handleAuthenticationIssues);
+ GitClient client = new GitClient(singleInstanceRepositoryRoot(repository), progressSupport, handleAuthenticationIssues);
client.setCallback(new CredentialsCallback());
return client;
}
+
+ public GitRepository getRepository (File repository) throws GitException {
+ return GitRepository.getInstance(singleInstanceRepositoryRoot(repository));
+ }
public RequestProcessor getRequestProcessor() {
return getRequestProcessor(null);
@@ -361,6 +361,15 @@
return topmost;
}
+
+ private File singleInstanceRepositoryRoot (File repository) {
+ // get the only instance for the repository folder, so we can synchronize on it
+ File repositoryFolder = getRepositoryRoot(repository);
+ if (repositoryFolder != null && repository.equals(repositoryFolder)) {
+ repository = repositoryFolder;
+ }
+ return repository;
+ }
private File getKnownParent(File file) {
File[] roots = knownRoots.toArray(new File[knownRoots.size()]);
--- git/src/org/netbeans/modules/git/client/GitClient.java
+++ git/src/org/netbeans/modules/git/client/GitClient.java
@@ -67,6 +67,7 @@
import org.netbeans.libs.git.GitRebaseResult;
import org.netbeans.libs.git.GitRefUpdateResult;
import org.netbeans.libs.git.GitRemoteConfig;
+import org.netbeans.libs.git.GitRepository.FastForwardOption;
import org.netbeans.libs.git.GitRepositoryState;
import org.netbeans.libs.git.GitRevertResult;
import org.netbeans.libs.git.GitRevisionInfo;
@@ -608,12 +609,12 @@
}, "log"); //NOI18N
}
- public GitMergeResult merge (final String revision, final ProgressMonitor monitor) throws GitException.CheckoutConflictException, GitException {
+ public GitMergeResult merge (final String revision, final FastForwardOption ffOption, final ProgressMonitor monitor) throws GitException.CheckoutConflictException, GitException {
return new CommandInvoker().runMethod(new Callable() {
@Override
public GitMergeResult call () throws Exception {
- return delegate.merge(revision, monitor);
+ return delegate.merge(revision, ffOption, monitor);
}
}, "merge"); //NOI18N
}
--- git/src/org/netbeans/modules/git/ui/fetch/PullAction.java
+++ git/src/org/netbeans/modules/git/ui/fetch/PullAction.java
@@ -63,6 +63,7 @@
import org.netbeans.libs.git.GitMergeResult;
import org.netbeans.libs.git.GitRebaseResult;
import org.netbeans.libs.git.GitRemoteConfig;
+import org.netbeans.libs.git.GitRepository;
import org.netbeans.libs.git.GitRevisionInfo;
import org.netbeans.libs.git.GitTransportUpdate;
import org.netbeans.modules.git.Git;
@@ -315,15 +316,16 @@
@Override
public ActionProgress call () throws GitException {
- boolean cont;
GitClient client = getClient();
File repository = getRepositoryRoot();
setDisplayName(Bundle.MSG_PullAction_merging());
+ MergeRevisionAction.MergeContext ctx = new MergeRevisionAction.MergeContext(branchToMerge, null);
+ MergeRevisionAction.MergeResultProcessor mrp = new MergeRevisionAction.MergeResultProcessor(client, repository, ctx, getLogger(), getProgressMonitor());
do {
- MergeRevisionAction.MergeResultProcessor mrp = new MergeRevisionAction.MergeResultProcessor(client, repository, branchToMerge, getLogger(), getProgressMonitor());
- cont = false;
+ ctx.setContinue(false);
+ GitRepository.FastForwardOption ffOption = null;
try {
- GitMergeResult result = client.merge(branchToMerge, getProgressMonitor());
+ GitMergeResult result = client.merge(branchToMerge, ffOption, getProgressMonitor());
mrp.processResult(result);
if (result.getMergeStatus() == GitMergeResult.MergeStatus.ALREADY_UP_TO_DATE
|| result.getMergeStatus() == GitMergeResult.MergeStatus.FAST_FORWARD
@@ -334,9 +336,9 @@
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Local modifications in WT during merge: {0} - {1}", new Object[] { repository, Arrays.asList(ex.getConflicts()) }); //NOI18N
}
- cont = mrp.resolveLocalChanges(ex.getConflicts());
+ ctx.setContinue(mrp.resolveLocalChanges(ex.getConflicts()));
}
- } while (cont && !isCanceled());
+ } while (ctx.isContinue() && !isCanceled());
return new ActionProgress.ActionResult(isCanceled(), true);
}
--- git/src/org/netbeans/modules/git/ui/merge/Bundle.properties
+++ git/src/org/netbeans/modules/git/ui/merge/Bundle.properties
@@ -47,7 +47,15 @@
MSG_MergeRevisionAction.result.conflict=Merge of HEAD with {0} produced conflicts in:\n
MSG_MergeRevisionAction.result.failed=Merge of HEAD with {0} failed. For more information see the output.
MSG_MergeRevisionAction.result.failedFiles=Merge of HEAD with {0} failed because of these files:\n
+MSG_MergeRevisionAction.result.aborted=Merge of HEAD with {0} failed because it requires a merge commit and only fast-forward merges were allowed.
MSG_MergeRevisionAction.result.unsupported=Merge unsupported.
LBL_MergeRevision.OKButton.text=Mer&ge
LBL_MergeRevision.title=Merge Revision
MergeRevisionPanel.jLabel1.text=Select a revision to merge into HEAD
+MergeRevision.ffoption.ff=Fast-forward if possible (--ff)
+MergeRevision.ffoption.ff.tt=Where possible merge will not create new commit but will move the branch to the merged commit.
+MergeRevision.ffoption.ffonly=Fast-forward only (--ff-only)
+MergeRevision.ffoption.ffonly.tt=Merge will fail if the merge commit is required (both branches have their own commits)
+MergeRevision.ffoption.noff=Always create commit (--no-ff)
+MergeRevision.ffoption.noff.tt=Merge will always create a merge commit, useful for merging feature branches.
+MergeRevisionPanel.ffModePanel.text=Select Fast-Forward mode
--- git/src/org/netbeans/modules/git/ui/merge/MergeRevision.java
+++ git/src/org/netbeans/modules/git/ui/merge/MergeRevision.java
@@ -47,6 +47,7 @@
import java.beans.PropertyChangeListener;
import java.io.File;
import javax.swing.JButton;
+import org.netbeans.libs.git.GitRepository.FastForwardOption;
import org.netbeans.modules.git.ui.repository.RevisionDialogController;
import org.netbeans.modules.git.utils.GitUtils;
import org.openide.DialogDescriptor;
@@ -59,16 +60,19 @@
* @author ondra
*/
public class MergeRevision {
- private MergeRevisionPanel panel;
- private RevisionDialogController revisionPicker;
+ private final MergeRevisionPanel panel;
+ private final RevisionDialogController revisionPicker;
private JButton okButton;
private DialogDescriptor dd;
private boolean valid = true;
+ private final FastForwardOption ffOption;
- MergeRevision (File repository, File[] roots, String initialRevision) {
+ MergeRevision (File repository, File[] roots, String initialRevision, FastForwardOption defaultFFOption) {
+ ffOption = defaultFFOption;
revisionPicker = new RevisionDialogController(repository, roots, initialRevision);
revisionPicker.setMergingInto(GitUtils.HEAD);
panel = new MergeRevisionPanel(revisionPicker.getPanel());
+ initFFOptions();
}
String getRevision() {
@@ -79,7 +83,8 @@
okButton = new JButton(NbBundle.getMessage(MergeRevision.class, "LBL_MergeRevision.OKButton.text")); //NOI18N
org.openide.awt.Mnemonics.setLocalizedText(okButton, okButton.getText());
dd = new DialogDescriptor(panel, NbBundle.getMessage(MergeRevision.class, "LBL_MergeRevision.title"), true, //NOI18N
- new Object[] { okButton, DialogDescriptor.CANCEL_OPTION }, okButton, DialogDescriptor.DEFAULT_ALIGN, new HelpCtx(MergeRevision.class), null);
+ new Object[] { okButton, DialogDescriptor.CANCEL_OPTION }, okButton, DialogDescriptor.DEFAULT_ALIGN,
+ new HelpCtx("org.netbeans.modules.git.ui.merge.MergeRevision"), null); //NOI18N
enableRevisionPanel();
revisionPicker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
@@ -94,6 +99,16 @@
return okButton == dd.getValue();
}
+ FastForwardOption getFFOption () {
+ if (panel.rbFFOptionOnly.isSelected()) {
+ return FastForwardOption.FAST_FORWARD_ONLY;
+ } else if (panel.rbFFOptionNever.isSelected()) {
+ return FastForwardOption.NO_FAST_FORWARD;
+ } else {
+ return FastForwardOption.FAST_FORWARD;
+ }
+ }
+
private void enableRevisionPanel () {
setValid(valid);
}
@@ -103,4 +118,18 @@
okButton.setEnabled(flag);
dd.setValid(flag);
}
+
+ private void initFFOptions () {
+ switch (ffOption) {
+ case FAST_FORWARD:
+ panel.rbFFOption.setSelected(true);
+ break;
+ case FAST_FORWARD_ONLY:
+ panel.rbFFOptionOnly.setSelected(true);
+ break;
+ case NO_FAST_FORWARD:
+ panel.rbFFOptionNever.setSelected(true);
+ break;
+ }
+ }
}
--- git/src/org/netbeans/modules/git/ui/merge/MergeRevisionAction.java
+++ git/src/org/netbeans/modules/git/ui/merge/MergeRevisionAction.java
@@ -58,6 +58,7 @@
import org.netbeans.modules.git.client.GitClient;
import org.netbeans.libs.git.GitException;
import org.netbeans.libs.git.GitMergeResult;
+import org.netbeans.libs.git.GitRepository.FastForwardOption;
import org.netbeans.libs.git.GitRevisionInfo;
import org.netbeans.libs.git.progress.ProgressMonitor;
import org.netbeans.modules.git.Git;
@@ -94,7 +95,13 @@
}
public void mergeRevision (final File repository, String preselectedRevision) {
- final MergeRevision mergeRevision = new MergeRevision(repository, new File[0], preselectedRevision);
+ FastForwardOption defaultFFOption = FastForwardOption.FAST_FORWARD;
+ try {
+ defaultFFOption = Git.getInstance().getRepository(repository).getDefaultFastForwardOption();
+ } catch (GitException ex) {
+ LOG.log(Level.INFO, null, ex);
+ }
+ final MergeRevision mergeRevision = new MergeRevision(repository, new File[0], preselectedRevision, defaultFFOption);
if (mergeRevision.show()) {
GitProgressSupport supp = new GitProgressSupport() {
private String revision;
@@ -109,20 +116,20 @@
client.addNotificationListener(new DefaultFileListener(new File[] { repository }));
revision = mergeRevision.getRevision();
LOG.log(Level.FINE, "Merging revision {0} into HEAD", revision); //NOI18N
- boolean cont;
- MergeResultProcessor mrp = new MergeResultProcessor(client, repository, revision, getLogger(), getProgressMonitor());
+ MergeContext ctx = new MergeContext(revision, mergeRevision.getFFOption());
+ MergeResultProcessor mrp = new MergeResultProcessor(client, repository, ctx, getLogger(), getProgressMonitor());
do {
- cont = false;
+ ctx.setContinue(false);
try {
- GitMergeResult result = client.merge(revision, getProgressMonitor());
+ GitMergeResult result = client.merge(revision, ctx.getFFOption(), getProgressMonitor());
mrp.processResult(result);
} catch (GitException.CheckoutConflictException ex) {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Local modifications in WT during merge: {0} - {1}", new Object[] { repository, Arrays.asList(ex.getConflicts()) }); //NOI18N
}
- cont = mrp.resolveLocalChanges(ex.getConflicts());
+ ctx.setContinue(mrp.resolveLocalChanges(ex.getConflicts()));
}
- } while (cont);
+ } while (ctx.isContinue() && !isCanceled());
return null;
}
}, repository);
@@ -139,20 +146,26 @@
}
}
+ @NbBundle.Messages({
+ "LBL_Merge.failed.title=Cannot Merge",
+ "MSG_Merge.failed.aborted.text=Merge requires a merge commit and cannot be a fast-forward merge.\n\n"
+ + "Do you want to restart the merge and allow merge commits (--ff option)."
+ })
public static class MergeResultProcessor extends ResultProcessor {
private final OutputLogger logger;
- private final String revision;
private final GitBranch current;
+ private final MergeContext context;
- public MergeResultProcessor (GitClient client, File repository, String revision, OutputLogger logger, ProgressMonitor pm) {
- super(client, repository, revision, pm);
+ public MergeResultProcessor (GitClient client, File repository, MergeContext context, OutputLogger logger, ProgressMonitor pm) {
+ super(client, repository, context.getRevision(), pm);
+ this.context = context;
this.current = RepositoryInfo.getInstance(repository).getActiveBranch();
- this.revision = revision;
this.logger = logger;
}
public void processResult (GitMergeResult result) {
+ String revision = context.getRevision();
StringBuilder sb = new StringBuilder(NbBundle.getMessage(MergeRevisionAction.class, "MSG_MergeRevisionAction.result", result.getMergeStatus().toString())); //NOI18N
GitRevisionInfo info = null;
if (result.getNewHead() != null) {
@@ -163,6 +176,7 @@
}
}
boolean logActions = false;
+ final Action openAction = logger.getOpenOutputAction();
switch (result.getMergeStatus()) {
case ALREADY_UP_TO_DATE:
sb.append(NbBundle.getMessage(MergeRevisionAction.class, "MSG_MergeRevisionAction.result.alreadyUpToDate", revision)); //NOI18N
@@ -183,7 +197,6 @@
resolveConflicts(result.getConflicts());
break;
case FAILED:
- final Action openAction = logger.getOpenOutputAction();
if (openAction != null) {
try {
EventQueue.invokeAndWait(new Runnable() {
@@ -201,6 +214,29 @@
DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
NbBundle.getMessage(MergeRevisionAction.class, "MSG_MergeRevisionAction.result.failed", revision), NotifyDescriptor.ERROR_MESSAGE)); //NOI18N
break;
+ case ABORTED:
+ Object o = DialogDisplayer.getDefault().notify(new NotifyDescriptor(Bundle.MSG_Merge_failed_aborted_text(),
+ Bundle.LBL_Merge_failed_title(), NotifyDescriptor.YES_NO_OPTION, NotifyDescriptor.QUESTION_MESSAGE,
+ new Object[] { NotifyDescriptor.YES_OPTION, NotifyDescriptor.NO_OPTION },
+ NotifyDescriptor.NO_OPTION));
+ if (o == NotifyDescriptor.YES_OPTION) {
+ context.setFFOption(FastForwardOption.FAST_FORWARD);
+ context.setContinue(true);
+ } else {
+ if (openAction != null) {
+ try {
+ EventQueue.invokeAndWait(new Runnable() {
+ @Override
+ public void run () {
+ openAction.actionPerformed(new ActionEvent(MergeResultProcessor.this, ActionEvent.ACTION_PERFORMED, null));
+ }
+ });
+ } catch (InterruptedException | InvocationTargetException ex) {
+ }
+ }
+ }
+ sb.append(NbBundle.getMessage(MergeRevisionAction.class, "MSG_MergeRevisionAction.result.aborted", revision)); //NOI18N
+ break;
case NOT_SUPPORTED:
sb.append(NbBundle.getMessage(MergeRevisionAction.class, "MSG_MergeRevisionAction.result.unsupported")); //NOI18N
DialogDisplayer.getDefault().notifyLater(new NotifyDescriptor.Message(
@@ -216,4 +252,36 @@
}
}
}
+
+ public static class MergeContext {
+ private FastForwardOption ffOption;
+ private final String revision;
+ private boolean cont;
+
+ public MergeContext (String revision, FastForwardOption ffOption) {
+ this.revision = revision;
+ this.ffOption = ffOption;
+ }
+
+ public String getRevision () {
+ return revision;
+ }
+
+ public FastForwardOption getFFOption () {
+ return ffOption;
+ }
+
+ private void setFFOption (FastForwardOption ffOption) {
+ this.ffOption = ffOption;
+ }
+
+ public void setContinue (boolean cont) {
+ this.cont = cont;
+ }
+
+ public boolean isContinue () {
+ return cont;
+ }
+
+ }
}
--- git/src/org/netbeans/modules/git/ui/merge/MergeRevisionPanel.form
+++ git/src/org/netbeans/modules/git/ui/merge/MergeRevisionPanel.form
@@ -1,11 +1,15 @@
-
+
--- git/src/org/netbeans/modules/git/ui/merge/MergeRevisionPanel.java
+++ git/src/org/netbeans/modules/git/ui/merge/MergeRevisionPanel.java
@@ -72,20 +72,61 @@
// //GEN-BEGIN:initComponents
private void initComponents() {
+ rbFFOptions = new javax.swing.ButtonGroup();
org.netbeans.modules.git.ui.repository.RevisionDialog revisionPickerDialog1 = this.revisionPanel;
jLabel1 = new javax.swing.JLabel();
+ jPanel1 = new javax.swing.JPanel();
- jLabel1.setText(org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevisionPanel.jLabel1.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevisionPanel.jLabel1.text")); // NOI18N
+ jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevisionPanel.ffModePanel.text"))); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(rbFFOption, org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevision.ffoption.ff")); // NOI18N
+ rbFFOption.setToolTipText(org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevision.ffoption.ff.tt")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(rbFFOptionNever, org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevision.ffoption.noff")); // NOI18N
+ rbFFOptionNever.setToolTipText(org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevision.ffoption.noff.tt")); // NOI18N
+
+ org.openide.awt.Mnemonics.setLocalizedText(rbFFOptionOnly, org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevision.ffoption.ffonly")); // NOI18N
+ rbFFOptionOnly.setToolTipText(org.openide.util.NbBundle.getMessage(MergeRevisionPanel.class, "MergeRevision.ffoption.ffonly.tt")); // NOI18N
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(rbFFOptionNever)
+ .addComponent(rbFFOptionOnly)
+ .addComponent(rbFFOption))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(rbFFOption)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(rbFFOptionOnly)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(rbFFOptionNever)
+ .addContainerGap())
+ );
+
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(revisionPickerDialog1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
- .addComponent(jLabel1)
- .addContainerGap(106, Short.MAX_VALUE))
- .addComponent(revisionPickerDialog1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(jLabel1)
+ .addGap(0, 0, Short.MAX_VALUE))
+ .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -93,13 +134,21 @@
.addContainerGap()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(revisionPickerDialog1, javax.swing.GroupLayout.DEFAULT_SIZE, 186, Short.MAX_VALUE))
+ .addComponent(revisionPickerDialog1, javax.swing.GroupLayout.DEFAULT_SIZE, 187, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
);
}// //GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
+ private javax.swing.JPanel jPanel1;
+ final javax.swing.JRadioButton rbFFOption = new javax.swing.JRadioButton();
+ final javax.swing.JRadioButton rbFFOptionNever = new javax.swing.JRadioButton();
+ final javax.swing.JRadioButton rbFFOptionOnly = new javax.swing.JRadioButton();
+ private javax.swing.ButtonGroup rbFFOptions;
// End of variables declaration//GEN-END:variables
}