blob: 6b4b9b5f7bc82504745625f0863ccac941e933f8 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2019 CEA LIST, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Nicolas FAUVERGUE (CEA LIST) nicolas.fauvergue@cea.fr - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.gitlight.git.utils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.egit.ui.internal.selection.SelectionUtils;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CreateBranchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand.ListMode;
import org.eclipse.jgit.api.MergeCommand;
import org.eclipse.jgit.api.MergeResult;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.ResetCommand.ResetType;
import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.api.errors.CanceledException;
import org.eclipse.jgit.api.errors.CheckoutConflictException;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.InvalidConfigurationException;
import org.eclipse.jgit.api.errors.InvalidRefNameException;
import org.eclipse.jgit.api.errors.InvalidRemoteException;
import org.eclipse.jgit.api.errors.NoFilepatternException;
import org.eclipse.jgit.api.errors.NoHeadException;
import org.eclipse.jgit.api.errors.NoMessageException;
import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
import org.eclipse.jgit.api.errors.RefNotAdvertisedException;
import org.eclipse.jgit.api.errors.RefNotFoundException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.api.errors.UnmergedPathsException;
import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.papyrus.gitlight.git.Activator;
import org.eclipse.uml2.uml.Element;
/**
* This class allows to define utils method for the git.
*/
@SuppressWarnings("restriction")
public class GitUtils {
/**
* This allows to open the git.
*
* @param repositoryPath
* The repository path.
* @return The opened git or <code>null</code>.
*/
public static Git openGit(final String repositoryPath) {
if (!repositoryPath.isEmpty()) {
try {
Git git = Git.open(new File(repositoryPath));
return git;
} catch (IOException e) {
Activator.getLogHelper().error("Error during the repository opening", e); //$NON-NLS-1$
}
}
return null;
}
/**
* This allows to get repository from project.
*
* @param project
* The project.
* @param shell
* The shell.
* @return The repository corresponding to the project.
*/
public static Repository getRepository(final Element element) {
final IFile umlFile = PapyrusFileUtils.getFile(element);
final IProject project = umlFile.getProject();
return null != project ? getRepository(project) : null;
}
/**
* This allows to get repository from project.
*
* @param project
* The project.
* @param shell
* The shell.
* @return The repository corresponding to the project.
*/
public static Repository getRepository(final IProject project) {
return SelectionUtils.getRepository(new StructuredSelection(project));
}
/**
* This allows to add a file for the commit.
*
* @param git
* The git.
* @param filePath
* The file path of file to add.
*/
public static void addFile(final Git git, final String filePath) {
if (null != git && !filePath.isEmpty()) {
try {
git.add().addFilepattern(filePath).call();
} catch (NoFilepatternException e) {
Activator.getLogHelper().error("Error while adding file: Pattern file is not correct", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while adding file: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to create a commit.
*
* @param git
* The git.
* @param message
* The commit message.
* @return The commit revision or <code>null</code>.
*/
public static RevCommit createCommit(final Git git, final String message) {
if (null != git) {
try {
return git.commit().setMessage(message).call();
} catch (NoHeadException e) {
Activator.getLogHelper().error("Error while creating commit: No head defined", e); //$NON-NLS-1$
} catch (NoMessageException e) {
Activator.getLogHelper().error("Error while creating commit: No defined message", e); //$NON-NLS-1$
} catch (UnmergedPathsException e) {
Activator.getLogHelper().error("Error while creating commit: Unknow error", e); //$NON-NLS-1$
} catch (ConcurrentRefUpdateException e) {
Activator.getLogHelper().error("Error while creating commit: Concurrent update", e); //$NON-NLS-1$
} catch (WrongRepositoryStateException e) {
Activator.getLogHelper().error("Error while creating commit: Wrong repository state", e); //$NON-NLS-1$
} catch (AbortedByHookException e) {
Activator.getLogHelper().error("Error while creating commit: Repository hooked", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while creating commit: Unknow error", e); //$NON-NLS-1$
}
}
return null;
}
/**
* This allows to push the commit.
*
* @param git
* The git.
*/
public static void pushCommit(final Git git) {
if (null != git) {
try {
git.push().call();
} catch (InvalidRemoteException e) {
Activator.getLogHelper().error("Error while pushing: Invalid remote", e); //$NON-NLS-1$
} catch (TransportException e) {
Activator.getLogHelper().error("Error while pushing: Connection problem", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while pushing: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to push a branch into repository.
*
* @param git
* The git.
* @param branchName
* The branch name to push.
*/
public static void pushBranchCommit(final Git git, final String branchName) {
if (null != git && !branchName.isEmpty()) {
try {
final PushCommand pushCommand = git.push();
pushCommand.setRemote(Constants.DEFAULT_REMOTE_NAME);
pushCommand.setRefSpecs(new RefSpec(branchName + ":" + branchName));
pushCommand.call();
} catch (InvalidRemoteException e) {
Activator.getLogHelper().error("Error while pushing: Invalid remote", e); //$NON-NLS-1$
} catch (TransportException e) {
Activator.getLogHelper().error("Error while pushing: Connection problem", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while pushing: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to create a local branch.
*
* @param git
* The git.
* @param branchName
* The branch name.
*/
public static void createBranch(final Git git, final String branchName) {
if (null != git && !branchName.isEmpty()) {
try {
git.checkout().setCreateBranch(true).setName(branchName).call();
} catch (RefAlreadyExistsException e) {
Activator.getLogHelper().error("Error while creating branch: Branch '" + branchName + "' already exists", e); //$NON-NLS-1$ //$NON-NLS-2$
} catch (RefNotFoundException e) {
Activator.getLogHelper().error("Error while creating branch: Reference not found", e); //$NON-NLS-1$
} catch (InvalidRefNameException e) {
Activator.getLogHelper().error("Error while creating branch: Invalid name", e); //$NON-NLS-1$
} catch (CheckoutConflictException e) {
Activator.getLogHelper().error("Error while creating branch: Checkout conflict", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while creating branch: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to create a local branch.
*
* @param git
* The git.
* @param branchName
* The branch name.
*/
public static void createBranch(final Git git, final String branchName, final String remoteBranchName) {
if (null != git && !branchName.isEmpty()) {
try {
git.checkout()
.setCreateBranch(true)
.setName(branchName)
.setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK)
.setStartPoint(Constants.DEFAULT_REMOTE_NAME + "/" + remoteBranchName) //$NON-NLS-1$
.call();
} catch (RefAlreadyExistsException e) {
Activator.getLogHelper().error("Error while creating branch: Branch '" + branchName + "' already exists", e); //$NON-NLS-1$ //$NON-NLS-2$
} catch (RefNotFoundException e) {
Activator.getLogHelper().error("Error while creating branch: Reference not found", e); //$NON-NLS-1$
} catch (InvalidRefNameException e) {
Activator.getLogHelper().error("Error while creating branch: Invalid name", e); //$NON-NLS-1$
} catch (CheckoutConflictException e) {
Activator.getLogHelper().error("Error while creating branch: Checkout conflict", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while creating branch: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to reset hard the current branch.
*
* @param git
* The git.
*/
public static void resetHardCurrentBranch(final Git git) {
if (null != git) {
try {
git.reset().setMode(ResetType.HARD).call();
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while creating branch: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* this allows to checkout an existing local branch.
*
* @param git
* The git.
* @param branchName
* The branch name.
*/
public static void checkoutExistingBranch(final Git git, final String branchName) {
if (null != git && !branchName.isEmpty()) {
try {
git.checkout().setCreateBranch(false).setName(branchName).call();
} catch (RefNotFoundException e) {
Activator.getLogHelper().error("Error while creating branch: Reference not found", e); //$NON-NLS-1$
} catch (InvalidRefNameException e) {
Activator.getLogHelper().error("Error while creating branch: Invalid name", e); //$NON-NLS-1$
} catch (CheckoutConflictException e) {
Activator.getLogHelper().error("Error while creating branch: Checkout conflict", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while creating branch: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to pull the current local branch.
*
* @param git
* The git.
*/
public static void pull(final Git git) {
if (null != git) {
try {
git.pull().call();
} catch (WrongRepositoryStateException e) {
Activator.getLogHelper().error("Error while pulling: Reference not found", e); //$NON-NLS-1$
} catch (InvalidConfigurationException e) {
Activator.getLogHelper().error("Error while pulling: Invalid configuration", e); //$NON-NLS-1$
} catch (InvalidRemoteException e) {
Activator.getLogHelper().error("Error while pulling: Invalid Remote", e); //$NON-NLS-1$
} catch (CanceledException e) {
Activator.getLogHelper().error("Error while pulling: Cancel request", e); //$NON-NLS-1$
} catch (RefNotFoundException e) {
Activator.getLogHelper().error("Error while pulling: Branch not found", e); //$NON-NLS-1$
} catch (RefNotAdvertisedException e) {
Activator.getLogHelper().error("Error while pulling: Branch not Advertised", e); //$NON-NLS-1$
} catch (NoHeadException e) {
Activator.getLogHelper().error("Error while pulling: No head", e); //$NON-NLS-1$
} catch (TransportException e) {
Activator.getLogHelper().error("Error while pulling: Transport Exception", e); //$NON-NLS-1$
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while pulling: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to delete a local branch.
*
* @param git
* The git.
* @param branchName
* The branch name to delete.
*/
public static void deleteLocalBranch(final Git git, final String branchName) {
if (null != git && !branchName.isEmpty()) {
try {
final List<Ref> branches = git.branchList().setListMode(ListMode.ALL).call();
branches.removeAll(git.branchList().setListMode(ListMode.REMOTE).call());
for (final Ref branch : branches) {
if (branch.getName().endsWith(branchName)) {
git.branchDelete().setBranchNames(branch.getName()).setForce(true).call();
break;
}
}
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while deleting local branch: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to get a local branch, return <code>null</code> if it does not exist.
*
* @param git
* The git.
* @param branchName
* The branch name to search.
* @return The branch found or <code>null</code>.
*/
public static Ref getLocalBranch(final Git git, final String branchName) {
if (null != git && !branchName.isEmpty()) {
try {
final List<Ref> branches = git.branchList().setListMode(ListMode.ALL).call();
branches.removeAll(git.branchList().setListMode(ListMode.REMOTE).call());
for (final Ref branch : branches) {
if (branch.getName().endsWith(branchName)) {
return branch;
}
}
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while deleting local branch: Unknow error", e); //$NON-NLS-1$
}
}
return null;
}
/**
* This allows to delete a remote branch.
*
* @param git
* The git.
* @param branchName
* The branch name to delete.
*/
public static void deleteBranch(final Git git, final String branchName) {
if (null != git && !branchName.isEmpty()) {
try {
git.branchDelete().setBranchNames(branchName).setForce(true).call();
final RefSpec refSpec = new RefSpec().setSource(null).setDestination(branchName);
git.push().setRefSpecs(refSpec).setRemote(Constants.DEFAULT_REMOTE_NAME).call();
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while deleting branch: Unknow error", e); //$NON-NLS-1$
}
}
}
/**
* This allows to get the list of reviews branches (branches started with 'Review_'.
*
* @param git
* The git.
* @return The list of reviews branches.
*/
public static List<Ref> getReviewBranches(final Git git) {
final List<Ref> reviewsBranches = new ArrayList<Ref>();
if (null != git) {
try {
final List<Ref> branches = git.branchList().setListMode(ListMode.REMOTE).call();
for (final Ref branch : branches) {
if (branch.getName().contains(GitConstants.CONTRIBUTION_BRANCH_PREFIX)) {
reviewsBranches.add(branch);
}
}
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while getting branches: Unknow error", e); //$NON-NLS-1$
}
}
return reviewsBranches;
}
/**
* This allows to get a branch depending to its name.
*
* @param git
* The git.
* @param branchName
* The branch name to search.
* @return The found branch or <code>null</code>.
*/
public static Ref getBranchForName(final Git git, final String branchName) {
if (null != git) {
try {
final List<Ref> branches = git.branchList().setListMode(ListMode.REMOTE).call();
for (final Ref branch : branches) {
if (branch.getName().endsWith(branchName)) {
return branch;
}
}
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while getting branches: Unknow error", e); //$NON-NLS-1$
}
}
return null;
}
/**
* This allows to get the current branch.
*
* @param git
* The git;
* @return The current branch or <code>null</code> if any problem.
*/
public static Ref getCurrentBranch(final Git git) {
if (null != git) {
try {
final String branchName = git.getRepository().getFullBranch();
return getLocalBranch(git, branchName);
} catch (IOException e) {
Activator.getLogHelper().error("Error while getting current branch: Unknow error", e); //$NON-NLS-1$
}
}
return null;
}
/**
* This allows to determinate if the current branch is up-do_date with the remote master.
*
* @param git
* The git.
* @return <code>true</code> if the current branch is up-do-date, <code>false</code> otherwise.
*/
public static boolean isCurrentBranchIsUpToDate(final Git git) {
if (null != git) {
try {
final String branchName = git.getRepository().getFullBranch();
final Ref currentBranch = getLocalBranch(git, branchName);
final Ref remoteBranch = getBranchForName(git, getBranchShortName(branchName));
if (null != currentBranch && null != remoteBranch) {
final RevCommit lastCommitOnLocal = getLastCommitOfBranch(git, currentBranch);
final RevCommit lastCommitOnRemote = getLastCommitOfBranch(git, remoteBranch);
return null != lastCommitOnLocal && null != lastCommitOnRemote && lastCommitOnLocal.getId().getName().equals(lastCommitOnRemote.getId().getName());
}
} catch (IOException e) {
Activator.getLogHelper().error("Error while getting current branch: Unknow error", e); //$NON-NLS-1$
}
}
return false;
}
/**
* This allows to merge a branch into master.
*
* @param git
* The git.
* @param masterBranchName
* The master branch (can be null or empty).
* @param branch
* The branch.
*/
public static void mergeBranch(final Git git, final String masterBranchName, final Ref branch) {
if (null != git && null != branch) {
try {
String masterName = null == masterBranchName || masterBranchName.isEmpty() ? Constants.MASTER : masterBranchName;
// Checkout the master branch
final CheckoutCommand checkoutCommand = (CheckoutCommand) git.checkout();
checkoutCommand.setName(masterName).setCreateBranch(false).call();
// Create the merge command
final MergeCommand mergeCommand = git.merge();
mergeCommand.include(branch);
MergeResult res = mergeCommand.call();
if (res.getMergeStatus().equals(MergeResult.MergeStatus.CONFLICTING)) {
throw new Exception("Cannot merge the branch due to conflict status"); //$NON-NLS-1$
}
} catch (GitAPIException e) {
Activator.getLogHelper().error("Error while merging branch: Unknow error", e); //$NON-NLS-1$
} catch (Exception e) {
Activator.getLogHelper().error(e);
}
}
}
/**
* Get the list commit of a branch.
*
* @param git
* The git.
* @param branch
* The branch.
* @return The last commit of the branch or <code>null</code>.
*/
public static RevCommit getLastCommitOfBranch(final Git git, final Ref branch) {
if (null != git && null != branch) {
try {
final RevWalk revWalk = new RevWalk(git.getRepository());
final RevCommit lastCommit = revWalk.parseCommit(branch.getObjectId());
revWalk.close();
return lastCommit;
} catch (MissingObjectException e) {
e.printStackTrace();
} catch (IncorrectObjectTypeException e) {
e.printStackTrace();
} catch (IOException e) {
Activator.getLogHelper().error("Error while getting last commit: Unknow error", e); //$NON-NLS-1$
}
}
return null;
}
/**
* This allows to get the short name (removing refs/heads or refs/remotes/origin) of a branch.
*
* @param branch
* The branch.
* @return The short name.
*/
public static String getBranchShortName(final Ref branch) {
return getBranchShortName(branch.getName());
}
/**
* This allows to get the short name (removing refs/heads or refs/remotes/origin) of a branch.
*
* @param branchName
* The branch name.
* @return The short name.
*/
public static String getBranchShortName(final String branchName) {
if (branchName.contains(Constants.R_REMOTES)) {
return branchName.substring(branchName.indexOf(Constants.R_REMOTES) + Constants.R_REMOTES.length() + Constants.DEFAULT_REMOTE_NAME.length() + 1);
} else if (branchName.contains(Constants.R_HEADS)) {
return branchName.substring(branchName.indexOf(Constants.R_HEADS) + Constants.R_HEADS.length());
}
return branchName;
}
}