org.eclipse.egit.core.op.CloneOperation.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.egit.core.op.CloneOperation.java

Source

/*******************************************************************************
 * Copyright (C) 2007, Dave Watson <dwatson@mimvista.com>
 * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
 * Copyright (C) 2008, Roger C. Soares <rogersoares@intelinet.com.br>
 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
 * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.eclipse.egit.core.op;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.util.Collection;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.egit.core.CoreText;
import org.eclipse.egit.core.EclipseGitProgressTransformer;
import org.eclipse.jgit.dircache.DirCacheCheckout;
import org.eclipse.jgit.errors.NotSupportedException;
import org.eclipse.jgit.errors.TransportException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepository;
import org.eclipse.jgit.transport.CredentialsProvider;
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.FileUtils;
import org.eclipse.osgi.util.NLS;

/**
 * Clones a repository from a remote location to a local location.
 */
public class CloneOperation {
    private final URIish uri;

    private final boolean allSelected;

    private final Collection<Ref> selectedBranches;

    private final File workdir;

    private final File gitdir;

    private final String branch;

    private final String remoteName;

    private final int timeout;

    private FileRepository local;

    private RemoteConfig remoteConfig;

    private FetchResult fetchResult;

    private CredentialsProvider credentialsProvider;

    /**
     * Create a new clone operation.
     *
     * @param uri
     *            remote we should fetch from.
     * @param allSelected
     *            true when all branches have to be fetched (indicates wildcard
     *            in created fetch refspec), false otherwise.
     * @param selectedBranches
     *            collection of branches to fetch. Ignored when allSelected is
     *            true.
     * @param workdir
     *            working directory to clone to. The directory may or may not
     *            already exist.
     * @param branch
     *            branch to initially clone from.
     * @param remoteName
     *            name of created remote config as source remote (typically
     *            named "origin").
     * @param timeout timeout in seconds
     */
    public CloneOperation(final URIish uri, final boolean allSelected, final Collection<Ref> selectedBranches,
            final File workdir, final String branch, final String remoteName, int timeout) {
        this.uri = uri;
        this.allSelected = allSelected;
        this.selectedBranches = selectedBranches;
        this.workdir = workdir;
        this.gitdir = new File(workdir, Constants.DOT_GIT);
        this.branch = branch;
        this.remoteName = remoteName;
        this.timeout = timeout;
    }

    /**
     * Sets a credentials provider
     * @param credentialsProvider
     */
    public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
        this.credentialsProvider = credentialsProvider;
    }

    /**
     * @param pm
     *            the monitor to be used for reporting progress and responding
     *            to cancellation. The monitor is never <code>null</code>
     * @throws InvocationTargetException
     * @throws InterruptedException
     */
    public void run(final IProgressMonitor pm) throws InvocationTargetException, InterruptedException {
        final IProgressMonitor monitor;
        if (pm == null)
            monitor = new NullProgressMonitor();
        else
            monitor = pm;

        try {
            monitor.beginTask(NLS.bind(CoreText.CloneOperation_title, uri), 5000);
            try {
                doInit(new SubProgressMonitor(monitor, 100));
                doFetch(new SubProgressMonitor(monitor, 4000));
                doCheckout(new SubProgressMonitor(monitor, 900));
            } finally {
                closeLocal();
            }
        } catch (final Exception e) {
            try {
                FileUtils.delete(workdir, FileUtils.RECURSIVE);
            } catch (IOException ioe) {
                throw new InvocationTargetException(ioe);
            }
            if (monitor.isCanceled())
                throw new InterruptedException();
            else
                throw new InvocationTargetException(e);
        } finally {
            monitor.done();
        }
    }

    /**
     * @return The git directory which will contain the repository
     */
    public File getGitDir() {
        return gitdir;
    }

    private void closeLocal() {
        if (local != null) {
            local.close();
            local = null;
        }
    }

    private void doInit(final IProgressMonitor monitor) throws URISyntaxException, IOException {
        monitor.setTaskName(CoreText.CloneOperation_initializingRepository);

        local = new FileRepository(gitdir);
        local.create();

        final RefUpdate head = local.updateRef(Constants.HEAD);
        head.disableRefLog();
        head.link(branch);

        remoteConfig = new RemoteConfig(local.getConfig(), remoteName);
        remoteConfig.addURI(uri);

        final String dst = Constants.R_REMOTES + remoteConfig.getName();
        RefSpec wcrs = new RefSpec();
        wcrs = wcrs.setForceUpdate(true);
        wcrs = wcrs.setSourceDestination(Constants.R_HEADS + "*", dst + "/*"); //$NON-NLS-1$ //$NON-NLS-2$

        if (allSelected) {
            remoteConfig.addFetchRefSpec(wcrs);
        } else {
            for (final Ref ref : selectedBranches)
                if (wcrs.matchSource(ref))
                    remoteConfig.addFetchRefSpec(wcrs.expandFromSource(ref));
        }

        // we're setting up for a clone with a checkout
        local.getConfig().setBoolean("core", null, "bare", false); //$NON-NLS-1$ //$NON-NLS-2$

        remoteConfig.update(local.getConfig());

        // branch is like 'Constants.R_HEADS + branchName', we need only
        // the 'branchName' part
        String branchName = branch.substring(Constants.R_HEADS.length());

        // setup the default remote branch for branchName
        local.getConfig().setString("branch", branchName, "remote", remoteName); //$NON-NLS-1$ //$NON-NLS-2$
        local.getConfig().setString("branch", branchName, "merge", branch); //$NON-NLS-1$ //$NON-NLS-2$

        local.getConfig().save();
    }

    private void doFetch(final IProgressMonitor monitor) throws NotSupportedException, TransportException {
        final Transport tn = Transport.open(local, remoteConfig);
        if (credentialsProvider != null)
            tn.setCredentialsProvider(credentialsProvider);
        tn.setTimeout(this.timeout);
        try {
            final EclipseGitProgressTransformer pm;
            pm = new EclipseGitProgressTransformer(monitor);
            fetchResult = tn.fetch(pm, null);
        } finally {
            tn.close();
        }
    }

    private void doCheckout(final IProgressMonitor monitor) throws IOException {
        final Ref head = fetchResult.getAdvertisedRef(branch);
        if (head == null || head.getObjectId() == null)
            return;

        final RevWalk rw = new RevWalk(local);
        final RevCommit mapCommit;
        try {
            mapCommit = rw.parseCommit(head.getObjectId());
        } finally {
            rw.release();
        }

        final RefUpdate u;

        u = local.updateRef(Constants.HEAD);
        u.setNewObjectId(mapCommit.getId());
        u.forceUpdate();

        monitor.setTaskName(CoreText.CloneOperation_checkingOutFiles);
        DirCacheCheckout dirCacheCheckout = new DirCacheCheckout(local, null, local.lockDirCache(),
                mapCommit.getTree());
        dirCacheCheckout.setFailOnConflict(true);
        boolean result = dirCacheCheckout.checkout();
        if (!result)
            // this should never happen when writing in an empty folder
            throw new IOException("Internal error occured on checking out files"); //$NON-NLS-1$
        monitor.setTaskName(CoreText.CloneOperation_writingIndex);
    }
}