Java tutorial
/* * z2env.org - (c) ZFabrik Software KG * * Licensed under Apache 2. * * www.z2-environment.net */ package org.z2env.impl.helper; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import org.eclipse.jgit.api.CreateBranchCommand.SetupUpstreamMode; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.RefSpec; import org.eclipse.jgit.transport.RemoteConfig; import org.eclipse.jgit.transport.RemoteSession; import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.FS; /** * Provides some useful static helpers like * {@link #isOriginReachable(URIish)}, {@link #isValidRepository(URIish)}, {@link #cloneRepository(URIish, File, CredentialsProvider, int)}, ... * * @author Udo Offermann * */ public class GitTools { /* network timeout in milliseconds */ private final static int NETWORK_TIMEOUT_MSEC = 5000; public enum ValidationResult { valid, invalid, cannotTell }; /** * Returns true if repoUri is local. If repoUri is remote the method returns true if the host is reachable. * * @param repoUri a Git-URIish * @return true if the host addressed by repoUri is reachable */ public static boolean isOriginReachable(URIish repoUri) { boolean result; if (repoUri.isRemote()) { try { result = InetAddress.getByName(repoUri.getHost()).isReachable(NETWORK_TIMEOUT_MSEC); } catch (Exception e) { result = false; } } else { // local is definitely reachable ;-) result = true; } return result; } /** * Checks whether the given repository is valid. * This works for local as well as for remote repositories. However only 'http' and 'ssh' based git-urls are supported at the moment. * The method returns {@link ValidationResult#cannotTell} for all other schemas. * * @param repoUri repository uri * * @return * <ul> * <li>{@link ValidationResult#valid}: repoUri points to a valid repository</li> * <li>{@link ValidationResult#invalid}: repoUri points to an invalid repository, or the remote side is not reachable</li> * <li>{@link ValidationResult#cannotTell}: repoUri cannot be validated (because there is no validation for repoUri's scheme available)</li> */ public static ValidationResult isValidRepository(URIish repoUri) { if (repoUri.isRemote()) { return isValidRemoteRepository(repoUri); } else { return isValidLocalRepository(repoUri); } } private static ValidationResult isValidLocalRepository(URIish repoUri) { ValidationResult result; try { if (Git.open(new File(repoUri.getPath())).getRepository().getObjectDatabase().exists()) { result = ValidationResult.valid; } else { result = ValidationResult.invalid; } } catch (IOException e) { result = ValidationResult.invalid; } return result; } private final static String INFO_REFS_PATH = "info/refs"; private static ValidationResult isValidRemoteRepository(URIish repoUri) { ValidationResult result; if (repoUri.getScheme().toLowerCase().startsWith("http")) { String path = repoUri.getPath(); String newPath = path.endsWith("/") ? path + INFO_REFS_PATH : path + "/" + INFO_REFS_PATH; URIish checkUri = repoUri.setPath(newPath); InputStream ins = null; try { URLConnection conn = new URL(checkUri.toString()).openConnection(); conn.setReadTimeout(NETWORK_TIMEOUT_MSEC); ins = conn.getInputStream(); result = ValidationResult.valid; } catch (Exception e) { result = ValidationResult.invalid; } finally { try { ins.close(); } catch (Exception e) { /* ignore */ } } } else if (repoUri.getScheme().toLowerCase().startsWith("ssh")) { RemoteSession ssh = null; Process exec = null; try { ssh = SshSessionFactory.getInstance().getSession(repoUri, null, FS.detect(), 5000); exec = ssh.exec("cd " + repoUri.getPath() + "; git rev-parse --git-dir", 5000); Integer exitValue = null; do { try { exitValue = exec.exitValue(); } catch (Exception e) { try { Thread.sleep(1000); } catch (Exception ee) { } } } while (exitValue == null); result = exitValue == 0 ? ValidationResult.valid : ValidationResult.invalid; } catch (Exception e) { result = ValidationResult.invalid; } finally { try { exec.destroy(); } catch (Exception e) { /* ignore */ } try { ssh.disconnect(); } catch (Exception e) { /* ignore */ } } } else { // TODO need to implement tests for other schemas result = ValidationResult.cannotTell; } return result; } /** * Clones the given remote repository into the given destination folder. The method clones all branches but doesn't perform a checkout. * * @param remoteUri URI of the remote repository * @param destFolder local destination folder * @param credentials user credentials * @return the cloned repository * @throws IOException if something went wrong */ public static Repository cloneRepository(URIish remoteUri, File destFolder, CredentialsProvider credentials, int timeout) throws IOException { // workaround for http://redmine.z2-environment.net/issues/902: // split clone into its piece in order to get the chance to set "core.autocrlf" Git gitResult; try { gitResult = Git.init().setBare(false).setDirectory(destFolder).call(); } catch (Exception e) { throw new IOException("Failed to initialize a new Git repository at " + destFolder.getAbsolutePath(), e); } Repository repo = gitResult.getRepository(); // setting "core.autocrlf=false" helps to solve http://redmine.z2-environment.net/issues/902 StoredConfig config = repo.getConfig(); config.setString(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_AUTOCRLF, String.valueOf(false)); // add origin - clone all branches RemoteConfig remoteCfg = null; try { remoteCfg = new RemoteConfig(config, "origin"); } catch (URISyntaxException e) { throw new IOException("Failed to configure origin repository", e); } remoteCfg.addURI(remoteUri); remoteCfg.addFetchRefSpec(new RefSpec("+refs/heads/*:refs/remotes/origin/*")); remoteCfg.update(config); config.save(); // fetch all branches from origin try { gitResult.fetch().setRemote("origin").setCredentialsProvider(credentials).setTimeout(timeout).call(); } catch (Exception e) { throw new IOException("Failed to fetch from origin!", e); } return repo; } /** * Switches to the given target branch in the given repository. If such a local branch exists the method performs a checkout on this branch. * Otherwise the branch name can be a remote branch in which case a local tracking branch is created. * @param repo the repository * @param targetBranch a local or remote branch name. * @throws IOException if something went wrong */ public static void switchBranch(Repository repo, String targetBranch) throws IOException { if (hasLocalBranch(repo, targetBranch)) { if (!targetBranch.equals(repo.getBranch())) { try { new Git(repo).checkout().setName(targetBranch).call(); } catch (Exception e) { throw new IOException("Failed to switch to branch '" + targetBranch + "' inside " + repo + "!"); } } } else if (hasRemoteBranch(repo, targetBranch)) { // create tracking branch try { new Git(repo).branchCreate().setName(targetBranch).setUpstreamMode(SetupUpstreamMode.TRACK) .setStartPoint(Constants.R_REMOTES + Constants.DEFAULT_REMOTE_NAME + "/" + targetBranch) .call(); } catch (Exception e) { throw new IOException( "Failed to create tracking branch '" + targetBranch + "' inside " + repo + "!"); } // switch to new branch try { new Git(repo).checkout().setName(targetBranch).call(); } catch (Exception e) { throw new IOException("Failed to switch to branch '" + targetBranch + "' inside " + repo + "!"); } } else { throw new IOException("No such branch '" + targetBranch + "' inside " + repo + "!"); } } private static boolean hasLocalBranch(Repository repo, String b) throws IOException { boolean result = null != repo.getRef("refs/heads/" + b); return result; } private static boolean hasRemoteBranch(Repository repo, String b) throws IOException { boolean result = null != repo.getRef("refs/remotes/origin/" + b); return result; } }