# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: /home/ondra/storage/netbeans/core-main # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: libs.git/apichanges.xml --- libs.git/apichanges.xml +++ libs.git/apichanges.xml @@ -112,6 +112,28 @@ New method for updating a reference (branch) to a new commit id. + + + + + +
    +
  • Git client accepts the parameter telling the merge command how + to proceed with regard to fast-forward commits. Users may require + either to enforce or to completely eliminate (and always create a merge commit) + fast-forward merges.
  • +
  • Git Repository instance allows users to query for the current default fast-forward + option via a new getter method.
  • +
+
+ + + + +
+ + + New method for updating a reference (branch) to a new commit id. Index: libs.git/manifest.mf --- libs.git/manifest.mf +++ libs.git/manifest.mf @@ -1,4 +1,4 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.libs.git/1 OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/git/Bundle.properties -OpenIDE-Module-Specification-Version: 1.25 +OpenIDE-Module-Specification-Version: 1.26 Index: libs.git/src/org/netbeans/libs/git/GitClient.java --- libs.git/src/org/netbeans/libs/git/GitClient.java +++ libs.git/src/org/netbeans/libs/git/GitClient.java @@ -53,6 +53,7 @@ import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryState; +import org.netbeans.libs.git.GitRepository.FastForwardOption; import org.netbeans.libs.git.GitRevisionInfo.GitFileInfo; import org.netbeans.libs.git.jgit.GitClassFactory; import org.netbeans.libs.git.jgit.JGitCredentialsProvider; @@ -867,7 +868,10 @@ } /** - * Merges a given revision with the current head + * Merges a given revision with the current head. + * + * Fast-forward option will default to the one stated in .git/config. + * * @param revision id of a revision to merge. * @param monitor progress monitor * @return result of the merge @@ -875,8 +879,22 @@ * @throws GitException an unexpected error occurs */ public GitMergeResult merge (String revision, ProgressMonitor monitor) throws GitException.CheckoutConflictException, GitException { + return merge(revision, null, monitor); + } + + /** + * Merges a given revision with the current head. + * @param revision id of a revision to merge. + * @param fastForward option telling merge to enforce or disable fast forward merges. + * @param monitor progress monitor + * @return result of the merge + * @throws GitException.CheckoutConflictException there are local modifications in Working Tree, merge fails in such a case + * @throws GitException an unexpected error occurs + * @since 1.26 + */ + public GitMergeResult merge (String revision, FastForwardOption fastForward, ProgressMonitor monitor) throws GitException.CheckoutConflictException, GitException { Repository repository = gitRepository.getRepository(); - MergeCommand cmd = new MergeCommand(repository, getClassFactory(), revision, monitor); + MergeCommand cmd = new MergeCommand(repository, getClassFactory(), revision, fastForward, monitor); cmd.execute(); return cmd.getResult(); } Index: libs.git/src/org/netbeans/libs/git/GitMergeResult.java --- libs.git/src/org/netbeans/libs/git/GitMergeResult.java +++ libs.git/src/org/netbeans/libs/git/GitMergeResult.java @@ -78,6 +78,16 @@ return "Fast-forward"; } }, + /** + * Fast forward merge cannot be executed, a commit is needed. + * @since 1.26 + */ + ABORTED { + @Override + public String toString() { + return "Aborted"; + } + }, ALREADY_UP_TO_DATE { @Override public String toString() { @@ -178,8 +188,6 @@ mergeStatus = MergeResult.MergeStatus.MERGED; } else if (mergeStatus == MergeResult.MergeStatus.MERGED_SQUASHED_NOT_COMMITTED) { mergeStatus = MergeResult.MergeStatus.MERGED; - } else if (mergeStatus == MergeResult.MergeStatus.ABORTED) { - mergeStatus = MergeResult.MergeStatus.FAILED; } else if (mergeStatus == MergeResult.MergeStatus.CHECKOUT_CONFLICT) { mergeStatus = MergeResult.MergeStatus.CONFLICTING; } Index: libs.git/src/org/netbeans/libs/git/GitRepository.java --- libs.git/src/org/netbeans/libs/git/GitRepository.java +++ libs.git/src/org/netbeans/libs/git/GitRepository.java @@ -45,6 +45,8 @@ import java.io.File; import java.util.Map; import java.util.WeakHashMap; +import org.eclipse.jgit.api.MergeCommand; +import org.eclipse.jgit.merge.MergeConfig; import org.eclipse.jgit.transport.SshSessionFactory; import org.netbeans.libs.git.jgit.JGitRepository; import org.netbeans.libs.git.jgit.JGitSshSessionFactory; @@ -77,7 +79,56 @@ private static final Map repositoryPool = new WeakHashMap(5); private final File repositoryLocation; private JGitRepository gitRepository; + + /** + * Option specifying how to deal with merges and merge commits. Required by + * {@link GitClient#merge(java.lang.String, org.netbeans.libs.git.GitRepository.FastForwardOption, org.netbeans.libs.git.progress.ProgressMonitor)}. + * To get the repository's default value, get it with {@link #getDefaultFastForwardOption()}. + * + * @since 1.26 + */ + public enum FastForwardOption { + /** + * Merge will not create a new commit if possible and only update the + * branch reference to the merged commit. This will usually happen if + * the merged commit is a descendant of the branch's head commit. + */ + FAST_FORWARD { + + @Override + public String toString () { + return "--ff"; //NOI18N + } + + }, + + /** + * Merge will fail if fast forward is impossible, no merge commit will + * be created under any circumstances. + */ + FAST_FORWARD_ONLY { + + @Override + public String toString () { + return "--ff-only"; //NOI18N + } + + }, + + /** + * Will always create a merge commit even if fast forward were possible. + */ + NO_FAST_FORWARD { + + @Override + public String toString () { + return "--no-ff"; //NOI18N + } + + }; + } + /** * Returns the instance of {@link GitRepository} representing an existing or not yet existing repository * specified by the given local folder. @@ -109,11 +160,43 @@ * @throws GitException when an error occurs while loading repository data from disk. */ public synchronized GitClient createClient () throws GitException { + getRepository(); + return createClient(gitRepository); + } + + /** + * Parses the repository configuration file and returns the default fast-forward merge + * option set for the repository and its current branch. + * + * @return the default fast-forward option for the current repository and the active branch. + * @throws GitException an error occurs + * @since 1.26 + */ + public FastForwardOption getDefaultFastForwardOption () throws GitException { + JGitRepository repository = getRepository(); + repository.increaseClientUsage(); + try { + MergeConfig cfg = MergeConfig.getConfigForCurrentBranch(repository.getRepository()); + MergeCommand.FastForwardMode mode = cfg.getFastForwardMode(); + switch (mode) { + case FF_ONLY: + return FastForwardOption.FAST_FORWARD_ONLY; + case NO_FF: + return FastForwardOption.NO_FAST_FORWARD; + default: + return FastForwardOption.FAST_FORWARD; + } + } finally { + repository.decreaseClientUsage(); + } + } + + private synchronized JGitRepository getRepository () { if (gitRepository == null) { gitRepository = new JGitRepository(repositoryLocation); SshSessionFactory.setInstance(JGitSshSessionFactory.getDefault()); } - return createClient(gitRepository); + return gitRepository; } /** Index: libs.git/src/org/netbeans/libs/git/jgit/commands/MergeCommand.java --- libs.git/src/org/netbeans/libs/git/jgit/commands/MergeCommand.java +++ libs.git/src/org/netbeans/libs/git/jgit/commands/MergeCommand.java @@ -52,6 +52,7 @@ import org.eclipse.jgit.lib.Repository; import org.netbeans.libs.git.GitException; import org.netbeans.libs.git.GitMergeResult; +import org.netbeans.libs.git.GitRepository.FastForwardOption; import org.netbeans.libs.git.jgit.GitClassFactory; import org.netbeans.libs.git.jgit.Utils; import org.netbeans.libs.git.progress.ProgressMonitor; @@ -64,9 +65,12 @@ private final String revision; private GitMergeResult result; private String commitMessage; + private final FastForwardOption ffOption; - public MergeCommand (Repository repository, GitClassFactory gitFactory, String revision, ProgressMonitor monitor) { + public MergeCommand (Repository repository, GitClassFactory gitFactory, String revision, + FastForwardOption ffOption, ProgressMonitor monitor) { super(repository, gitFactory, monitor); + this.ffOption = ffOption; this.revision = revision; } @@ -74,6 +78,7 @@ protected void run () throws GitException { Repository repository = getRepository(); org.eclipse.jgit.api.MergeCommand command = new Git(repository).merge(); + setFastForward(command); Ref ref = null; try { ref = repository.getRef(revision); @@ -106,7 +111,11 @@ @Override protected String getCommandDescription () { - return new StringBuilder("git merge ").append(revision).toString(); //NOI18N + StringBuilder sb = new StringBuilder("git merge "); //NOI18N + if (ffOption != null) { + sb.append(ffOption).append(" "); //NOI18N + } + return sb.append(revision).toString(); } public GitMergeResult getResult () { @@ -127,4 +136,22 @@ } throw new GitException(original); } + + private void setFastForward (org.eclipse.jgit.api.MergeCommand cmd) { + if (ffOption == null) { + // will fall back on the config default + return; + } + switch (ffOption) { + case FAST_FORWARD: + cmd.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.FF); + break; + case FAST_FORWARD_ONLY: + cmd.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.FF_ONLY); + break; + case NO_FAST_FORWARD: + cmd.setFastForward(org.eclipse.jgit.api.MergeCommand.FastForwardMode.NO_FF); + break; + } + } } Index: libs.git/src/org/netbeans/libs/git/jgit/commands/PullCommand.java --- libs.git/src/org/netbeans/libs/git/jgit/commands/PullCommand.java +++ libs.git/src/org/netbeans/libs/git/jgit/commands/PullCommand.java @@ -85,7 +85,7 @@ fetch.setCredentialsProvider(getCredentialsProvider()); fetch.run(); this.updates = fetch.getUpdates(); - MergeCommand merge = new MergeCommand(getRepository(), getClassFactory(), branchToMerge, monitor); + MergeCommand merge = new MergeCommand(getRepository(), getClassFactory(), branchToMerge, null, monitor); merge.setCommitMessage("branch \'" + findRemoteBranchName() + "\' of " + fetch.getResult().getURI().setUser(null).setPass(null).toString()); merge.run(); this.mergeResult = merge.getResult(); Index: libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/MergeTest.java --- libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/MergeTest.java +++ libs.git/test/unit/src/org/netbeans/libs/git/jgit/commands/MergeTest.java @@ -56,6 +56,7 @@ import org.netbeans.libs.git.GitException; import org.netbeans.libs.git.GitMergeResult; import org.netbeans.libs.git.GitMergeResult.MergeStatus; +import org.netbeans.libs.git.GitRepository; import org.netbeans.libs.git.GitRevisionInfo; import org.netbeans.libs.git.GitTransportUpdate; import org.netbeans.libs.git.SearchCriteria; @@ -381,4 +382,89 @@ client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR); client.merge(BRANCH_NAME, NULL_PROGRESS_MONITOR); } + + public void testMergeNoFastForward () throws Exception { + File f = new File(workDir, "file"); + write(f, "init"); + add(f); + commit(f); + + GitClient client = getClient(workDir); + client.createBranch(BRANCH_NAME, Constants.MASTER, NULL_PROGRESS_MONITOR); + client.checkoutRevision(BRANCH_NAME, true, NULL_PROGRESS_MONITOR); + write(f, BRANCH_NAME); + add(f); + GitRevisionInfo info = client.commit(new File[] { f }, "change on branch", null, null, NULL_PROGRESS_MONITOR); + client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR); + + assertEquals("init", read(f)); + + GitMergeResult result = client.merge(BRANCH_NAME, NULL_PROGRESS_MONITOR); + assertEquals(MergeStatus.FAST_FORWARD, result.getMergeStatus()); + assertEquals(BRANCH_NAME, read(f)); + + SearchCriteria crit = new SearchCriteria(); + crit.setRevisionTo(Constants.MASTER); + GitRevisionInfo[] logs = client.log(crit, NULL_PROGRESS_MONITOR); + assertEquals(2, logs.length); + assertEquals(logs[0].getRevision(), info.getRevision()); + + // continue working on branch + client.checkoutRevision(BRANCH_NAME, true, NULL_PROGRESS_MONITOR); + remove(false, f); + client.commit(new File[] { f }, "delete on branch", null, null, NULL_PROGRESS_MONITOR); + client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR); + + assertEquals(BRANCH_NAME, read(f)); + + result = client.merge(BRANCH_NAME, GitRepository.FastForwardOption.NO_FAST_FORWARD, NULL_PROGRESS_MONITOR); + assertEquals(MergeStatus.MERGED, result.getMergeStatus()); + assertFalse(f.exists()); + + crit = new SearchCriteria(); + crit.setRevisionTo(Constants.MASTER); + logs = client.log(crit, NULL_PROGRESS_MONITOR); + assertEquals(4, logs.length); + assertEquals(2, logs[0].getParents().length); + } + + public void testMergeFFOnly () throws Exception { + File f1 = new File(workDir, "file1"); + File f2 = new File(workDir, "file2"); + write(f1, "init"); + write(f2, "init"); + add(f1, f2); + commit(f1, f2); + + GitClient client = getClient(workDir); + client.createBranch(BRANCH_NAME, Constants.MASTER, NULL_PROGRESS_MONITOR); + client.checkoutRevision(BRANCH_NAME, true, NULL_PROGRESS_MONITOR); + write(f1, BRANCH_NAME); + add(f1); + client.commit(new File[] { f1 }, "change on branch", null, null, NULL_PROGRESS_MONITOR); + client.checkoutRevision(Constants.MASTER, true, NULL_PROGRESS_MONITOR); + write(f2, "another change"); + add(f2); + client.commit(new File[] { f2 }, "change on master", null, null, NULL_PROGRESS_MONITOR); + + GitMergeResult result = client.merge(BRANCH_NAME, GitRepository.FastForwardOption.FAST_FORWARD_ONLY, NULL_PROGRESS_MONITOR); + // no merge commits allowed => FAIL + assertEquals(MergeStatus.ABORTED, result.getMergeStatus()); + + // test also config files + assertEquals(GitRepository.FastForwardOption.FAST_FORWARD, GitRepository.getInstance(workDir).getDefaultFastForwardOption()); + + StoredConfig cfg = repo.getConfig(); + cfg.setEnum(ConfigConstants.CONFIG_KEY_MERGE, null, + ConfigConstants.CONFIG_KEY_FF, org.eclipse.jgit.api.MergeCommand.FastForwardMode.Merge.ONLY); + cfg.save(); + assertEquals(GitRepository.FastForwardOption.FAST_FORWARD_ONLY, GitRepository.getInstance(workDir).getDefaultFastForwardOption()); + result = client.merge(BRANCH_NAME, NULL_PROGRESS_MONITOR); + // no merge commits allowed => FAIL + assertEquals(MergeStatus.ABORTED, result.getMergeStatus()); + + result = client.merge(BRANCH_NAME, GitRepository.FastForwardOption.FAST_FORWARD, NULL_PROGRESS_MONITOR); + // merge commits allowed => OK + assertEquals(MergeStatus.MERGED, result.getMergeStatus()); + } }