org.zanata.sync.plugin.git.service.impl.GitSyncService.java Source code

Java tutorial

Introduction

Here is the source code for org.zanata.sync.plugin.git.service.impl.GitSyncService.java

Source

/*
 * Copyright 2015, Red Hat, Inc. and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.zanata.sync.plugin.git.service.impl;

import org.eclipse.jgit.api.AddCommand;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.CommitCommand;
import org.eclipse.jgit.api.CreateBranchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.ListBranchCommand;
import org.eclipse.jgit.api.MergeCommand;
import org.eclipse.jgit.api.PushCommand;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.StatusCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zanata.sync.common.model.Credentials;
import org.zanata.sync.common.exception.RepoSyncException;
import org.zanata.sync.plugin.git.service.RepoSyncService;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * @author Patrick Huang <a href="mailto:pahuang@redhat.com">pahuang@redhat.com</a>
 */
public class GitSyncService implements RepoSyncService<String> {
    private static final Logger log = LoggerFactory.getLogger(GitSyncService.class);

    private final Credentials<String> credentials;

    // TODO use CDI injection
    public GitSyncService(Credentials<String> credentials) {
        this.credentials = credentials;
    }

    public Credentials<String> getCredentials() {
        return credentials;
    }

    public void cloneRepo(String repoUrl, String branch, File destPath) throws RepoSyncException {
        if (isGitCloneAlreadyBeenDone(destPath)) {
            log.info("git repo already exists. Skipping clone");
            doGitFetch(destPath);
        } else {
            // directory is not a git repository
            log.debug("doing git clone");
            doGitClone(repoUrl, destPath);
        }
        checkOutBranch(destPath, branch);
    }

    private void doGitFetch(File destPath) {
        try {
            Git git = Git.open(destPath);
            git.fetch().setRemote("origin").call();
        } catch (IOException ioe) {
            // ignore
        } catch (Exception e) {
            log.error("fail to call git pull", e);
            throw new RepoSyncException(e);
        }
    }

    private boolean isGitCloneAlreadyBeenDone(File folder) {
        return /*RepositoryCache.FileKey.isGitRepository(folder, FS.DETECTED) &&*/
        hasAtLeastOneReference(folder);
    }

    private static boolean hasAtLeastOneReference(File folder) {
        try {
            Repository repo = Git.open(folder).getRepository();
            return repo.getAllRefs().values().stream().anyMatch(ref -> ref.getObjectId() != null);
        } catch (IOException e) {
            return false;
        }
    }

    private void doGitClone(String repoUrl, File destPath) {
        destPath.mkdirs();
        CloneCommand clone = Git.cloneRepository();
        clone.setBare(false);
        clone.setCloneAllBranches(false);
        clone.setDirectory(destPath).setURI(repoUrl);
        UsernamePasswordCredentialsProvider user = new UsernamePasswordCredentialsProvider(
                getCredentials().getUsername(), getCredentials().getSecret());
        clone.setCredentialsProvider(user);
        try {
            clone.call();
        } catch (GitAPIException e) {
            throw new RepoSyncException(e);
        }
    }

    private void checkOutBranch(File destPath, String branch) {
        try {
            Git git = Git.open(destPath);
            List<Ref> refs = git.branchList().setListMode(ListBranchCommand.ListMode.ALL).call();
            /* refs will have name like these:
            refs/heads/master
            refs/remotes/origin/master
            refs/remotes/origin/zanata
            */
            Optional<Ref> localBranchRef = Optional.empty();
            Optional<Ref> remoteBranchRef = Optional.empty();
            for (Ref ref : refs) {
                String refName = ref.getName();
                if (refName.equals("refs/heads/" + branch)) {
                    localBranchRef = Optional.of(ref);
                }
                if (refName.equals("refs/remotes/origin/" + branch)) {
                    remoteBranchRef = Optional.of(ref);
                }
            }

            if (branch.equals("master")) {
                log.debug("merging origin/master");
                git.checkout().setName("master").call();
                git.merge().setFastForward(MergeCommand.FastForwardMode.FF_ONLY).include(remoteBranchRef.get())
                        .call();
                return;
            }

            /**
             * If branch found in local and is not master, delete it, create new local branch from remote.
             * If branch does not exists in remote, create new local branch based on master branch.
             */
            Ref ref;
            if (localBranchRef.isPresent()) {
                git.checkout().setName("master").setForce(true).call();
                git.branchDelete().setBranchNames(branch).call();
            }

            if (remoteBranchRef.isPresent()) {
                ref = git.branchCreate().setForce(true).setName(branch).setStartPoint("origin/" + branch)
                        .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM).call();
            } else {
                ref = git.branchCreate().setForce(true).setName(branch).setStartPoint("origin/master")
                        .setUpstreamMode(CreateBranchCommand.SetupUpstreamMode.SET_UPSTREAM).call();
            }
            log.debug("checked out {}", ref);
            git.checkout().setName(branch).call();
            if (log.isDebugEnabled()) {
                log.debug("current branch is: {}", git.getRepository().getBranch());
            }
        } catch (IOException | GitAPIException e) {
            throw new RepoSyncException(e);
        }
    }

    @Override
    public void syncTranslationToRepo(String repoUrl, String branch, File baseDir) throws RepoSyncException {
        try {
            Git git = Git.open(baseDir);
            StatusCommand statusCommand = git.status();
            Status status = statusCommand.call();
            Set<String> uncommittedChanges = status.getUncommittedChanges();
            uncommittedChanges.addAll(status.getUntracked());
            if (!uncommittedChanges.isEmpty()) {
                log.info("uncommitted files in git repo: {}", uncommittedChanges);
                AddCommand addCommand = git.add();
                addCommand.addFilepattern(".");
                addCommand.call();

                log.info("commit changed files");
                CommitCommand commitCommand = git.commit();
                commitCommand.setCommitter("Zanata Auto Repo Sync", "zanata-users@redhat.com");
                commitCommand.setMessage("Zanata Auto Repo Sync (pushing translations)");
                commitCommand.call();

                log.info("push to remote repo");
                PushCommand pushCommand = git.push();
                UsernamePasswordCredentialsProvider user = new UsernamePasswordCredentialsProvider(
                        getCredentials().getUsername(), getCredentials().getSecret());
                pushCommand.setCredentialsProvider(user);
                pushCommand.call();
            } else {
                log.info("nothing changed so nothing to do");
            }
        } catch (IOException e) {
            throw new RepoSyncException("failed opening " + baseDir + " as git repo", e);
        } catch (GitAPIException e) {
            throw new RepoSyncException("Failed committing translations into the repo", e);
        }
    }
}