org.webcat.core.git.GitCloner.java Source code

Java tutorial

Introduction

Here is the source code for org.webcat.core.git.GitCloner.java

Source

/*==========================================================================*\
 |  $Id: GitCloner.java,v 1.1 2011/05/13 19:46:57 aallowat Exp $
 |*-------------------------------------------------------------------------*|
 |  Copyright (C) 2011 Virginia Tech
 |
 |  This file is part of Web-CAT.
 |
 |  Web-CAT is free software; you can redistribute it and/or modify
 |  it under the terms of the GNU Affero General Public License as published
 |  by the Free Software Foundation; either version 3 of the License, or
 |  (at your option) any later version.
 |
 |  Web-CAT 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 Affero General Public License
 |  along with Web-CAT; if not, see <http://www.gnu.org/licenses/>.
\*==========================================================================*/

package org.webcat.core.git;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.log4j.Logger;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.InitCommand;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.Transport;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FS;

//-------------------------------------------------------------------------
/**
 * Provides functionality to clone a repository, creating it for the first time
 * if necessary.
 *
 * @author Tony Allevato
 * @author Last changed by $Author: aallowat $
 * @version $Revision: 1.1 $, $Date: 2011/05/13 19:46:57 $
 */
public class GitCloner {
    //~ Constructors ..........................................................

    // ----------------------------------------------------------
    public GitCloner(File repositoryToClone, File destination) {
        this.repositoryToClone = repositoryToClone;
        this.destination = destination;
    }

    //~ Methods ...............................................................

    // ----------------------------------------------------------
    public Repository cloneRepository(boolean forcePull) throws IOException {
        if (!destination.exists()) {
            forcePull = true;
            log.debug("Working copy created for the first time: " + destination);
        }

        Repository wcRepository = createWorkingCopyRepositoryIfNecessary(destination, repositoryToClone);

        if (forcePull) {
            FetchResult fetchResult = doFetch(wcRepository);
            Ref head = guessHead(fetchResult);
            doCheckout(wcRepository, head);

            log.debug("Pulled to update working copy: " + destination);
        }

        return wcRepository;
    }

    // ----------------------------------------------------------
    private Repository createWorkingCopyRepositoryIfNecessary(File location, File remoteDir) throws IOException {
        Repository wcRepository;

        try {
            wcRepository = RepositoryCache.open(FileKey.lenient(location, FS.DETECTED));
        } catch (RepositoryNotFoundException e) {
            // Create the repository from scratch.

            if (!location.exists()) {
                location.mkdirs();
            }

            InitCommand init = Git.init();
            init.setDirectory(location);
            init.setBare(false);
            wcRepository = init.call().getRepository();

            StoredConfig config = wcRepository.getConfig();
            config.setBoolean("core", null, "bare", false);

            try {
                RefSpec refSpec = new RefSpec().setForceUpdate(true).setSourceDestination(Constants.R_HEADS + "*",
                        Constants.R_REMOTES + "origin" + "/*");

                RemoteConfig remoteConfig = new RemoteConfig(config, "origin");
                remoteConfig.addURI(new URIish(remoteDir.toString()));
                remoteConfig.addFetchRefSpec(refSpec);
                remoteConfig.update(config);
            } catch (URISyntaxException e2) {
                // Do nothing.
            }

            config.save();
        }

        return wcRepository;
    }

    // ----------------------------------------------------------
    private FetchResult doFetch(Repository repository) throws NotSupportedException, TransportException {
        FetchResult result = null;

        try {
            Transport transport = Transport.open(repository, "origin");

            try {
                result = transport.fetch(NullProgressMonitor.INSTANCE, null);
            } finally {
                transport.close();
            }
        } catch (URISyntaxException e) {
            // Do nothing.
        }

        return result;
    }

    // ----------------------------------------------------------
    private void doCheckout(Repository repository, Ref branch) throws IOException {
        if (!Constants.HEAD.equals(branch.getName())) {
            RefUpdate refUpdate = repository.updateRef(Constants.HEAD);
            refUpdate.disableRefLog();
            refUpdate.link(branch.getName());
        }

        RevCommit commit = parseCommit(repository, branch);
        RefUpdate refUpdate = repository.updateRef(Constants.HEAD);
        refUpdate.setNewObjectId(commit);
        refUpdate.forceUpdate();

        DirCache dirCache = repository.lockDirCache();
        DirCacheCheckout checkout = new DirCacheCheckout(repository, dirCache, commit.getTree());
        checkout.checkout();
    }

    // ----------------------------------------------------------
    private RevCommit parseCommit(Repository repository, Ref branch)
            throws MissingObjectException, IncorrectObjectTypeException, IOException {
        RevWalk rw = new RevWalk(repository);
        RevCommit commit;

        try {
            commit = rw.parseCommit(branch.getObjectId());
        } finally {
            rw.release();
        }

        return commit;
    }

    // ----------------------------------------------------------
    private Ref guessHead(FetchResult result) {
        Ref idHEAD = result.getAdvertisedRef(Constants.HEAD);
        List<Ref> availableRefs = new ArrayList<Ref>();
        Ref head = null;

        for (Ref ref : result.getAdvertisedRefs()) {
            String name = ref.getName();

            if (!name.startsWith(Constants.R_HEADS)) {
                continue;
            }

            availableRefs.add(ref);

            if (idHEAD == null || head != null) {
                continue;
            }

            if (ref.getObjectId().equals(idHEAD.getObjectId())) {
                head = ref;
            }
        }

        Collections.sort(availableRefs, RefComparator.INSTANCE);

        if (idHEAD != null && head == null) {
            head = idHEAD;
        }

        return head;
    }

    //~ Static/instance variables .............................................

    private File repositoryToClone;
    private File destination;

    private static final Logger log = Logger.getLogger(GitCloner.class);
}