All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.g2forge.gearbox.git.HGit Maven / Gradle / Ivy

package com.g2forge.gearbox.git;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.TransportConfigCallback;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.internal.storage.file.FileRepository;
import org.eclipse.jgit.lib.CheckoutEntry;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ReflogEntry;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.transport.JschConfigSessionFactory;
import org.eclipse.jgit.transport.OpenSshConfig;
import org.eclipse.jgit.transport.OpenSshConfig.Host;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.SshTransport;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.util.FS;

import com.g2forge.alexandria.java.core.helpers.HCollection;
import com.g2forge.alexandria.java.io.RuntimeIOException;
import com.g2forge.alexandria.java.marker.Helpers;
import com.g2forge.gearbox.git.ssh.PasswordSSHUserInfo;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.UtilityClass;

@Helpers
@UtilityClass
public class HGit {
	protected static void knownHosts(final JSch jsch, FS fs) throws JSchException {
		final File home = fs.userHome();
		if (home == null) return;

		final Path knownHosts = home.toPath().resolve(HSSH.SSHDIR).resolve(HSSH.KNOWN_HOSTS);
		try {
			try (final InputStream in = Files.newInputStream(knownHosts)) {
				jsch.setKnownHosts(in);
			}
		} catch (IOException exception) {
			// No known hosts file, no real problem
		}
	}

	public static TransportConfigCallback createTransportConfig(String key, String password) {
		final SshSessionFactory sessionFactory = new JschConfigSessionFactory() {
			@Override
			protected void configure(Host hc, Session session) {
				session.setUserInfo(new PasswordSSHUserInfo(password));
			}

			@Override
			protected JSch getJSch(final OpenSshConfig.Host hc, FS fs) throws JSchException {
				final JSch retVal = new JSch();
				knownHosts(retVal, fs);
				retVal.addIdentity(key);
				return retVal;
			}
		};

		return new TransportConfigCallback() {
			@Override
			public void configure(Transport transport) {
				if (transport instanceof SshTransport) {
					final SshTransport sshTransport = ((SshTransport) transport);
					sshTransport.setSshSessionFactory(sessionFactory);
				}
			}
		};
	}

	public static ZonedDateTime getTime(ReflogEntry entry) {
		final PersonIdent who = entry.getWho();
		return ZonedDateTime.ofInstant(who.getWhen().toInstant(), who.getTimeZone().toZoneId());
	}

	public static class ReflogTimeComparator implements Comparator {
		@Override
		public int compare(ReflogEntry arg0, ReflogEntry arg1) {
			return HGit.getTime(arg1).compareTo(HGit.getTime(arg0));
		}
	}

	@AllArgsConstructor
	public static class WhoReflogEntry implements ReflogEntry {
		@Getter
		protected final PersonIdent who;

		@Override
		public String getComment() {
			throw new UnsupportedOperationException();
		}

		@Override
		public ObjectId getNewId() {
			throw new UnsupportedOperationException();
		}

		@Override
		public ObjectId getOldId() {
			throw new UnsupportedOperationException();
		}

		@Override
		public CheckoutEntry parseCheckout() {
			throw new UnsupportedOperationException();
		}
	}

	public static Git createGit(Path root) {
		return createGit(root, true);
	}

	public static Git createGit(Path root, boolean create) {
		try {
			final Path gitDir = getGitFile(root);
			final boolean actuallyCreate = create && !Files.isDirectory(gitDir);
			if (actuallyCreate) Files.createDirectories(root);
			final FileRepository repository = new FileRepository(gitDir.toFile());
			if (actuallyCreate) repository.create();

			final Git git = new Git(repository);
			if (create) {
				if (!Files.isDirectory(gitDir)) try {
					Git.init().setGitDir(gitDir.toFile()).call();
				} catch (GitAPIException exception) {
					throw new RuntimeException("Failed to initialize new git repository in " + root, exception);
				}
			}
			return git;
		} catch (IOException exception) {
			throw new RuntimeIOException(exception);
		}
	}

	public static Path getGitFile(Path root) {
		return root.resolve(GIT_DIRECTORY);
	}

	public static final String GIT_DIRECTORY = ".git";

	/**
	 * Test if the given git repository has a branch with the specified name.
	 * 
	 * @param git The git repository to check for the branch
	 * @param branch The name of the branch to look for
	 * @return true if a branch with the specified name exists in the specified repository
	 */
	public static boolean isBranch(final Git git, final String branch) {
		try {
			return git.getRepository().findRef(Constants.R_HEADS + branch) != null;
		} catch (IOException exception) {
			throw new RuntimeIOException(String.format("Failed to check for branch \"%1$s\" in repository \"%2$s\"!", branch, git.getRepository().getDirectory().toPath()), exception);
		}
	}

	public static String getMyRemote(final Git git) {
		final Set remotes = git.getRepository().getRemoteNames();
		if (remotes.size() == 1) return HCollection.getOne(remotes);
		if (remotes.size() == 2) {
			if (remotes.contains(Constants.DEFAULT_REMOTE_NAME)) {
				final Set modify = new HashSet<>(remotes);
				modify.remove(Constants.DEFAULT_REMOTE_NAME);
				return HCollection.getOne(modify);
			}
		}
		throw new IllegalStateException(String.format("Cannot automatically guess your git remote from among %1$s!", remotes));
	}

	/**
	 * Set the local branch to track the specified remote branch.
	 * 
	 * @param git The git repository to configure.
	 * @param localBranch The local branch to set tracking for.
	 * @param remote The remote repository to track a branch in.
	 * @param remoteBranch The branch in the remote repository, or null to use the branch named localBranch in the remote repository.
	 * @throws IOException
	 */
	public static void setTracking(Git git, String localBranch, String remote, String remoteBranch) throws IOException {
		final StoredConfig config = git.getRepository().getConfig();
		config.setString(ConfigConstants.CONFIG_BRANCH_SECTION, localBranch, ConfigConstants.CONFIG_KEY_REMOTE, remote);
		config.setString(ConfigConstants.CONFIG_BRANCH_SECTION, localBranch, ConfigConstants.CONFIG_KEY_MERGE, Constants.R_HEADS + (remoteBranch == null ? localBranch : remoteBranch));
		config.save();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy