/* * 2016 Red Hat, Inc. and/or its affiliates. * * Licensed 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. */ package org.uberfire.java.nio.fs.jgit.util.commands; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.merge.ThreeWayMerger; import org.eclipse.jgit.revwalk.RevCommit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.uberfire.java.nio.fs.jgit.util.JGitUtil; import org.uberfire.java.nio.fs.jgit.util.exceptions.GitException; import static org.uberfire.commons.validation.PortablePreconditions.checkNotEmpty; import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull; /** * Implements Git Merge command between branches in a bare repository. * Branches needs to be part of the same repository, you cannot merge * branches from different repositories (or forks). * This command is based on Git Cherry Pick command. * It returns the list of commits cherry picked. */ public class Merge extends GitCommand { private final Git git; private final String sourceBranch; private final String targetBranch; private Logger logger = LoggerFactory.getLogger(Merge.class); public Merge(final Git git, final String sourceBranch, final String targetBranch) { this.git = checkNotNull("git", git); this.sourceBranch = checkNotEmpty("sourceBranch", sourceBranch); this.targetBranch = checkNotEmpty("targetBranch", targetBranch); } @Override public Optional<List<String>> execute() { this.existsBranch(git, sourceBranch); this.existsBranch(git, targetBranch); final Repository repo = git.getRepository(); final RevCommit lastSourceCommit = JGitUtil.getLastCommit(git, sourceBranch); final RevCommit lastTargetCommit = JGitUtil.getLastCommit(git, targetBranch); final RevCommit commonAncestor = JGitUtil.getCommonAncestor(git, lastSourceCommit, lastTargetCommit); final List<RevCommit> commits = JGitUtil.getCommits(git, sourceBranch, commonAncestor, lastSourceCommit); Collections.reverse(commits); final String[] commitsIDs = commits.stream().map(elem -> elem.getName()).toArray(String[]::new); canMerge(repo, commonAncestor, lastSourceCommit, lastTargetCommit, sourceBranch, targetBranch); JGitUtil.cherryPick(repo, targetBranch, commitsIDs); if (logger.isDebugEnabled()) { logger.debug("Merging commits from <{}> to <{}>", sourceBranch, targetBranch); } return Optional.ofNullable(Arrays.asList(commitsIDs)); } private void canMerge(final Repository repo, final RevCommit commonAncestor, final RevCommit sourceCommitTree, final RevCommit targetCommitTree, final String sourceBranch, final String targetBranch) { try { ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(repo, true); merger.setBase(commonAncestor); boolean canMerge = merger.merge(sourceCommitTree, targetCommitTree); if (!canMerge) { throw new GitException(String.format("Cannot merge braches from <%s> to <%s>, merge conflicts", sourceBranch, targetBranch)); } } catch (IOException e) { throw new GitException(String.format("Cannot merge braches from <%s> to <%s>, merge conflicts", sourceBranch, targetBranch), e); } } private void existsBranch(final Git git, final String branch) { if (JGitUtil.getBranch(git, branch) == null) { throw new GitException(String.format("Branch <<%s>> does not exists", branch)); } } }