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

ch.sbb.releasetrain.state.git.GitRepoImpl Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements;
 * and to You under the Apache License, Version 2.0.
 */
package ch.sbb.releasetrain.state.git;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.CheckoutCommand;
import org.eclipse.jgit.api.CreateBranchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.PullResult;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.TransportException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.PushResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteRefUpdate;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;

/**
 * Access a git repository via JGit.
 *
 * @author u206123 (Florian Seidl)
 * @author u203244 (Daniel Marthaler)
 * @since 0.0.1, 2016
 */
@Slf4j
public final class GitRepoImpl implements GitRepo {

    private final File gitDir;

    private final String url;

    private final String branch;

    private final String user;

    private final String password;


    GitRepoImpl(final String url, final String branch, final String user, final String password, final File tempDir) {
        this.url = url;
        this.branch = branch;
        this.user = user;
        this.password = password;
        this.gitDir = tempDir;
    }

    boolean isCloned() {
        return new File(gitDir, ".git").exists();
    }

    @Override
    public File directory() {
        return gitDir;
    }

    @Override
    public void reset() {
        try {
            FileUtils.deleteDirectory(this.gitDir);
        } catch (IOException e) {
            log.error("not able to delete folder: " + this.gitDir, e);
        }
    }

    public void cloneOrPull() {
        callWithRetry(c -> doCloneOrPull(), 0);
    }

    private void doCloneOrPull() throws IOException, GitAPIException {
        if (isCloned()) {
            Git git = pull(gitOpen());
            checkoutOrCreateBranch(git);
        }
        else {
            Git git = gitClone();
            checkoutOrCreateBranch(git);
        }
    }

    public void addCommitPush() {
        callWithRetry(c -> doAddCommitPush(), 0);
    }

    public void doAddCommitPush() throws IOException, GitAPIException {
        Git git = gitOpen();
        gitOpen().add()
                .addFilepattern(".")
                .call();
        git.commit()
                .setMessage("Automatic commit by releasetrain")
                .call();
        git.push()
                .setCredentialsProvider(credentialsProvider())
                .call();
    }

    private void callWithRetry(final GitConsumer call, int retry) {
        try {
            call.accept(null);
        }
        catch (IOException | TransportException e) {
            if (retry < 3) {
                try {
                    Thread.sleep(1000L * (retry + 1)); // back off
                    callWithRetry(call, retry + 1);
                } catch (InterruptedException e1) { // bad luck...
                }
            } else {
                throw new GitException(String.format("Git operation falied after %d retries", retry), e);
            }
        }
        catch (GitAPIException e) {
            throw new GitException("Git operation failed", e);
        }
    }

    Git pull(Git git) throws GitAPIException {
        if(remoteBranchExists(git)) {
            PullResult result = git
                    .pull()
                    .setStrategy(MergeStrategy.THEIRS)
                    .call();
            if (result.isSuccessful()) {
                return git;
            }
            else {
                throw new GitException("Pull failed: " + result.toString());
            }
        }
        else {
            return git;
        }
    }

    Git checkoutOrCreateBranch(final Git git) throws GitAPIException, IOException {
        if (!branch.equals(git.getRepository().getBranch())) {
            CheckoutCommand checkoutCommand = git.checkout()
                    .setCreateBranch(true)
                    .setName(branch)
                    .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.TRACK);

            if (remoteBranchExists(git)) {
                checkoutCommand.setStartPoint("origin/" + branch);
            }
            checkoutCommand.call();
        }
        return git;
    }

    private boolean remoteBranchExists(final Git git) throws GitAPIException {
        List branchRefs = git.branchList().setListMode(ListBranchCommand.ListMode.ALL).call();
        final String refsHeadBranch = "refs/remotes/origin/" + branch;
        for (Ref branchRef : branchRefs) {
            if (refsHeadBranch.equals(branchRef.getName())) {
                return true;
            }
        }
        return false;
    }

    private Git gitClone() throws GitAPIException {
        return Git.cloneRepository()
                .setURI(url)
                .setCredentialsProvider(credentialsProvider())
                .setDirectory(gitDir)
                .call();
    }

    private Git gitOpen() throws IOException {
        return Git.open(new File(gitDir, ".git"));
    }

    private CredentialsProvider credentialsProvider() {
        return new UsernamePasswordCredentialsProvider(user, password);
    }

    /**
     * Use with care!
     */
    public boolean deleteBranch() {
        if (!branch.startsWith("feature/")) {
            throw new GitException("Can only delete feature branch.");
        }
        try {

            final Git git = gitOpen();
            git.checkout().
                    setCreateBranch(true).
                    setName("feature/temp_" + UUID.randomUUID()).
                    call();
            List deletedBranches = git.branchDelete().setBranchNames(branch).setForce(true).call();
            if (deletedBranches.size() == 1) {
                // delete branch 'branchToDelete' on remote 'origin'
                RefSpec refSpec = new RefSpec()
                        .setSource(null)
                        .setDestination("refs/heads/" + branch);
                Iterable results = git.push()
                        .setCredentialsProvider(credentialsProvider())
                        .setRefSpecs(refSpec)
                        .setRemote("origin")
                        .call();
                for (PushResult result : results) {
                    RemoteRefUpdate myUpdate = result.getRemoteUpdate("refs/heads/" + branch);
                    if (myUpdate.isDelete() && myUpdate.getStatus() == RemoteRefUpdate.Status.OK) {
                        return true;
                    }
                }
            }
            return false;
        } catch (IOException | GitAPIException e) {
            throw new GitException("Delete branch failed", e);
        }
    }


    @FunctionalInterface
    private interface GitConsumer {
        void accept(T t) throws IOException, GitAPIException;
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy