/*==========================================================================*\ | $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); }