org.craftercms.commons.git.utils.GitUtils Maven / Gradle / Ivy
/*
* Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.craftercms.commons.git.utils;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.craftercms.commons.git.auth.GitAuthenticationConfigurator;
import org.eclipse.jgit.api.*;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.URIish;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.eclipse.jgit.api.ResetCommand.ResetType.HARD;
import static org.eclipse.jgit.lib.Constants.HEAD;
/**
* Utility methods for Git operations.
*
* @author avasquez
* @author Phil Nguyen
*/
public abstract class GitUtils {
private static final Logger logger = LoggerFactory.getLogger(GitUtils.class);
public static final String GIT_FOLDER_NAME = ".git";
public static final String GIT_INDEX_NAME = "index";
public static final String GIT_LOCK_NAME = GIT_INDEX_NAME + ".lock";
public static final String CORE_CONFIG_SECTION = "core";
public static final String BIG_FILE_THRESHOLD_CONFIG_PARAM = "bigFileThreshold";
public static final String COMPRESSION_CONFIG_PARAM = "compression";
public static final String FILE_MODE_CONFIG_PARAM = "fileMode";
public static final String BIG_FILE_THRESHOLD_DEFAULT = "20m";
public static final int COMPRESSION_DEFAULT = 0;
public static final boolean FILE_MODE_DEFAULT = false;
private GitUtils() {
}
/**
* Opens the Git repository at the specified location.
*
* @param localRepositoryFolder the folder where the Git repository is
* @return the Git instance used to handle the repository
* @throws IOException if an error occurs
*/
public static Git openRepository(File localRepositoryFolder) throws IOException {
return Git.open(localRepositoryFolder);
}
/**
* Clones a remote repository into a specific local folder.
*
* @param remoteName the name of the remote
* @param remoteUrl the URL of the remote. This should be a legal Git URL.
* @param branch the branch which should be cloned
* @param authConfigurator the {@link GitAuthenticationConfigurator} class used to configure the authentication
* with the remote repository
* @param localFolder the local folder into which the remote repository should be cloned
* @param bigFileThreshold the value of the Git {@code core.bigFileThreshold} config property
* @param compression the value of the Git {@code core.compression} config property
* @param fileMode the value of the Git {@code core.fileMode} config property
* @return the Git instance used to handle the cloned repository
* @throws GitAPIException if a Git related error occurs
* @throws IOException if an IO error occurs
*/
public static Git cloneRemoteRepository(String remoteName, String remoteUrl, String branch,
GitAuthenticationConfigurator authConfigurator, File localFolder,
String bigFileThreshold, Integer compression,
Boolean fileMode) throws GitAPIException, IOException {
CloneCommand command = Git.cloneRepository();
command.setRemote(remoteName);
command.setURI(remoteUrl);
command.setDirectory(localFolder);
if (StringUtils.isNotEmpty(branch)) {
command.setCloneAllBranches(false);
command.setBranchesToClone(Collections.singletonList(Constants.R_HEADS + branch));
command.setBranch(branch);
}
if (authConfigurator != null) {
authConfigurator.configureAuthentication(command);
}
Git git = command.call();
StoredConfig config = git.getRepository().getConfig();
if (StringUtils.isEmpty(bigFileThreshold)) {
bigFileThreshold = BIG_FILE_THRESHOLD_DEFAULT;
}
if (compression == null) {
compression = COMPRESSION_DEFAULT;
}
if (fileMode == null) {
fileMode = FILE_MODE_DEFAULT;
}
config.setString(CORE_CONFIG_SECTION, null, BIG_FILE_THRESHOLD_CONFIG_PARAM, bigFileThreshold);
config.setInt(CORE_CONFIG_SECTION, null, COMPRESSION_CONFIG_PARAM, compression);
config.setBoolean(CORE_CONFIG_SECTION, null, FILE_MODE_CONFIG_PARAM, fileMode);
config.save();
return git;
}
/**
* Execute a Git pull.
*
* @param git the Git instance used to handle the repository
* @param remoteName the name of the remote where to pull from
* @param remoteUrl the URL of the remote (remote will be set to the URL)
* @param branch the branch to pull
* @param mergeStrategy the merge strategy to use
* @param authConfigurator the {@link GitAuthenticationConfigurator} class used to configure the authentication
* with the remote repository
* @return the result of the pull
* @throws GitAPIException if a Git related error occurs
* @throws URISyntaxException if the remote URL is invalid
*/
public static PullResult pull(Git git, String remoteName, String remoteUrl, String branch,
MergeStrategy mergeStrategy, GitAuthenticationConfigurator authConfigurator)
throws GitAPIException, URISyntaxException {
addRemote(git, remoteName, remoteUrl);
PullCommand command = git.pull();
command.setRemote(remoteName);
command.setRemoteBranchName(branch);
if (mergeStrategy != null) {
command.setStrategy(mergeStrategy);
}
if (authConfigurator != null) {
authConfigurator.configureAuthentication(command);
}
return command.call();
}
/**
* Executes a git push.
*
* @param git the Git instance used to handle the repository
* @param remote remote name or URL
* @param pushAll if the push should push all local branches
* @param remoteBranch the remote remoteBranch being pushed to
* @param authConfigurator the {@link GitAuthenticationConfigurator} class used to configure the authentication
* with the remote
* repository
* @param force sets the force preference for the push
* @return the result of the push
* @throws GitAPIException if a Git related error occurs
*/
public static Iterable push(Git git, String remote, boolean pushAll, String localBranch,
String remoteBranch, GitAuthenticationConfigurator authConfigurator,
boolean force) throws GitAPIException {
PushCommand push = git.push();
push.setRemote(remote);
push.setForce(force);
if (pushAll) {
push.setPushAll();
} else if (StringUtils.isNotEmpty(remoteBranch)) {
push.setRefSpecs(new RefSpec(localBranch + ":" + Constants.R_HEADS + remoteBranch));
}
if (authConfigurator != null) {
authConfigurator.configureAuthentication(push);
}
return push.call();
}
/**
* Executes a git gc.
* @param repoPath full path of the repository
* @throws GitAPIException if there is an error running the command
* @throws IOException if there is an error opening the repository
*/
public static void cleanup(String repoPath) throws GitAPIException, IOException {
try (Git git = openRepository(new File(repoPath))) {
git.gc().call();
}
}
/**
* Executes a git reset to discard all uncommitted changes
* @param git the git repository
* @throws GitAPIException if there is an error performing the reset
*/
public static void discardAllChanges(Git git) throws GitAPIException {
git.reset().setMode(HARD).setRef(HEAD).call();
}
/**
* Adds a remote if it doesn't exist. If the remote exists but the URL is different, updates the URL
*
* @param git the Git repo
* @param remoteName the name oif the remote
* @param remoteUrl the URL of the remote
*
* @throws GitAPIException if a Git error occurs
* @throws URISyntaxException if the remote URL is an invalid Git URL
*/
private static void addRemote(Git git, String remoteName, String remoteUrl) throws GitAPIException,
URISyntaxException {
String currentUrl = git.getRepository().getConfig().getString("remote", remoteName, "url");
if (StringUtils.isNotEmpty(currentUrl)) {
if (!currentUrl.equals(remoteUrl)) {
RemoteSetUrlCommand remoteSetUrl = git.remoteSetUrl();
remoteSetUrl.setRemoteName(remoteName);
remoteSetUrl.setRemoteUri(new URIish(remoteUrl));
remoteSetUrl.call();
}
} else {
RemoteAddCommand remoteAdd = git.remoteAdd();
remoteAdd.setName(remoteName);
remoteAdd.setUri(new URIish(remoteUrl));
remoteAdd.call();
}
}
public static boolean isRepositoryLocked(String repoPath) {
Path path = Paths.get(repoPath, GIT_FOLDER_NAME, GIT_LOCK_NAME);
return Files.exists(path);
}
public static void unlock(String repoPath) throws IOException {
deleteFile(Paths.get(repoPath, GIT_FOLDER_NAME, GIT_LOCK_NAME));
}
public static void deleteGitIndex(String repoPath) throws IOException {
deleteFile(Paths.get(repoPath, GIT_FOLDER_NAME, GIT_INDEX_NAME));
}
protected static void deleteFile(Path file) throws IOException {
try {
Files.deleteIfExists(file);
} catch (IOException e) {
logger.debug("Error deleting file {}, forcing delete", file, e);
FileUtils.forceDelete(file.toFile());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy