Package org.eclipse.jgit.gitrepo

Source Code of org.eclipse.jgit.gitrepo.RepoCommand

/*
* Copyright (C) 2014, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
*   copyright notice, this list of conditions and the following
*   disclaimer in the documentation and/or other materials provided
*   with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
*   names of its contributors may be used to endorse or promote
*   products derived from this software without specific prior
*   written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.gitrepo;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.channels.FileChannel;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.GitCommand;
import org.eclipse.jgit.api.SubmoduleAddCommand;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.gitrepo.internal.RepoText;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.util.FileUtils;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

/**
* A class used to execute a repo command.
*
* This will parse a repo XML manifest, convert it into .gitmodules file and the
* repository config file.
*
* If called against a bare repository, it will replace all the existing content
* of the repository with the contents populated from the manifest.
*
* repo manifest allows projects overlapping, e.g. one project's path is
* "foo" and another project's path is "foo/bar". This won't
* work in git submodule, so we'll skip all the sub projects
* ("foo/bar" in the example) while converting.
*
* @see <a href="https://code.google.com/p/git-repo/">git-repo project page</a>
* @since 3.4
*/
public class RepoCommand extends GitCommand<RevCommit> {

  private String path;
  private String uri;
  private String groups;
  private String branch;
  private PersonIdent author;
  private RemoteReader callback;
  private InputStream inputStream;
  private IncludedFileReader includedReader;

  private List<Project> bareProjects;
  private Git git;
  private ProgressMonitor monitor;

  /**
   * A callback to get ref sha1 of a repository from its uri.
   *
   * We provided a default implementation {@link DefaultRemoteReader} to
   * use ls-remote command to read the sha1 from the repository and clone the
   * repository to read the file. Callers may have their own quicker
   * implementation.
   *
   * @since 3.4
   */
  public interface RemoteReader {
    /**
     * Read a remote ref sha1.
     *
     * @param uri
     *            The URI of the remote repository
     * @param ref
     *            The ref (branch/tag/etc.) to read
     * @return the sha1 of the remote repository
     * @throws GitAPIException
     */
    public ObjectId sha1(String uri, String ref) throws GitAPIException;

    /**
     * Read a file from a remote repository.
     *
     * @param uri
     *            The URI of the remote repository
     * @param ref
     *            The ref (branch/tag/etc.) to read
     * @param path
     *            The relative path (inside the repo) to the file to read
     * @return the file content.
     * @throws GitAPIException
     * @throws IOException
     * @since 3.5
     */
    public byte[] readFile(String uri, String ref, String path)
        throws GitAPIException, IOException;
  }

  /** A default implementation of {@link RemoteReader} callback. */
  public static class DefaultRemoteReader implements RemoteReader {
    public ObjectId sha1(String uri, String ref) throws GitAPIException {
      Map<String, Ref> map = Git
          .lsRemoteRepository()
          .setRemote(uri)
          .callAsMap();
      Ref r = RefDatabase.findRef(map, ref);
      return r != null ? r.getObjectId() : null;
    }

    public byte[] readFile(String uri, String ref, String path)
        throws GitAPIException, IOException {
      File dir = FileUtils.createTempDir("jgit_", ".git", null); //$NON-NLS-1$ //$NON-NLS-2$
      Repository repo = Git
          .cloneRepository()
          .setBare(true)
          .setDirectory(dir)
          .setURI(uri)
          .call()
          .getRepository();
      try {
        return readFileFromRepo(repo, ref, path);
      } finally {
        FileUtils.delete(dir, FileUtils.RECURSIVE);
      }
    }

    /**
     * Read a file from the repository
     *
     * @param repo
     *            The repository containing the file
     * @param ref
     *            The ref (branch/tag/etc.) to read
     * @param path
     *            The relative path (inside the repo) to the file to read
     * @return the file's content
     * @throws GitAPIException
     * @throws IOException
     * @since 3.5
     */
    protected byte[] readFileFromRepo(Repository repo,
        String ref, String path) throws GitAPIException, IOException {
      ObjectReader reader = repo.newObjectReader();
      byte[] result;
      try {
        ObjectId oid = repo.resolve(ref + ":" + path); //$NON-NLS-1$
        result = reader.open(oid).getBytes(Integer.MAX_VALUE);
      } finally {
        reader.release();
      }
      return result;
    }
  }

  /**
   * A callback to read included xml files.
   *
   * @since 3.5
   */
  public interface IncludedFileReader {
    /**
     * Read a file from the same base dir of the manifest xml file.
     *
     * @param path
     *            The relative path to the file to read
     * @return the {@code InputStream} of the file.
     * @throws GitAPIException
     * @throws IOException
     */
    public InputStream readIncludeFile(String path)
        throws GitAPIException, IOException;
  }

  private static class CopyFile {
    final Repository repo;
    final String path;
    final String src;
    final String dest;

    CopyFile(Repository repo, String path, String src, String dest) {
      this.repo = repo;
      this.path = path;
      this.src = src;
      this.dest = dest;
    }

    void copy() throws IOException {
      File srcFile = new File(repo.getWorkTree(),
          path + "/" + src); //$NON-NLS-1$
      File destFile = new File(repo.getWorkTree(), dest);
      FileInputStream input = new FileInputStream(srcFile);
      try {
        FileOutputStream output = new FileOutputStream(destFile);
        try {
          FileChannel channel = input.getChannel();
          output.getChannel().transferFrom(channel, 0, channel.size());
        } finally {
          output.close();
        }
      } finally {
        input.close();
      }
    }
  }

  private static class Project implements Comparable<Project> {
    final String name;
    final String path;
    final String revision;
    final String remote;
    final Set<String> groups;
    final List<CopyFile> copyfiles;

    Project(String name, String path, String revision,
        String remote, String groups) {
      this.name = name;
      if (path != null)
        this.path = path;
      else
        this.path = name;
      this.revision = revision;
      this.remote = remote;
      this.groups = new HashSet<String>();
      if (groups != null && groups.length() > 0)
        this.groups.addAll(Arrays.asList(groups.split(","))); //$NON-NLS-1$
      copyfiles = new ArrayList<CopyFile>();
    }

    void addCopyFile(CopyFile copyfile) {
      copyfiles.add(copyfile);
    }

    String getPathWithSlash() {
      if (path.endsWith("/")) //$NON-NLS-1$
        return path;
      else
        return path + "/"; //$NON-NLS-1$
    }

    boolean isAncestorOf(Project that) {
      return that.getPathWithSlash().startsWith(this.getPathWithSlash());
    }

    @Override
    public boolean equals(Object o) {
      if (o instanceof Project) {
        Project that = (Project) o;
        return this.getPathWithSlash().equals(that.getPathWithSlash());
      }
      return false;
    }

    @Override
    public int hashCode() {
      return this.getPathWithSlash().hashCode();
    }

    public int compareTo(Project that) {
      return this.getPathWithSlash().compareTo(that.getPathWithSlash());
    }
  }

  private static class XmlManifest extends DefaultHandler {
    private final RepoCommand command;
    private final String filename;
    private final String baseUrl;
    private final Map<String, String> remotes;
    private final Set<String> plusGroups;
    private final Set<String> minusGroups;
    private List<Project> projects;
    private String defaultRemote;
    private String defaultRevision;
    private IncludedFileReader includedReader;
    private int xmlInRead;
    private Project currentProject;

    XmlManifest(RepoCommand command, IncludedFileReader includedReader,
        String filename, String baseUrl, String groups) {
      this.command = command;
      this.includedReader = includedReader;
      this.filename = filename;

      // Strip trailing /s to match repo behavior.
      int lastIndex = baseUrl.length() - 1;
      while (lastIndex >= 0 && baseUrl.charAt(lastIndex) == '/')
        lastIndex--;
      this.baseUrl = baseUrl.substring(0, lastIndex + 1);

      remotes = new HashMap<String, String>();
      projects = new ArrayList<Project>();
      plusGroups = new HashSet<String>();
      minusGroups = new HashSet<String>();
      if (groups == null || groups.length() == 0 || groups.equals("default")) { //$NON-NLS-1$
        // default means "all,-notdefault"
        minusGroups.add("notdefault"); //$NON-NLS-1$
      } else {
        for (String group : groups.split(",")) { //$NON-NLS-1$
          if (group.startsWith("-")) //$NON-NLS-1$
            minusGroups.add(group.substring(1));
          else
            plusGroups.add(group);
        }
      }
    }

    void read(InputStream inputStream) throws IOException {
      xmlInRead++;
      final XMLReader xr;
      try {
        xr = XMLReaderFactory.createXMLReader();
      } catch (SAXException e) {
        throw new IOException(JGitText.get().noXMLParserAvailable);
      }
      xr.setContentHandler(this);
      try {
        xr.parse(new InputSource(inputStream));
      } catch (SAXException e) {
        IOException error = new IOException(
              RepoText.get().errorParsingManifestFile);
        error.initCause(e);
        throw error;
      }
    }

    @Override
    public void startElement(
        String uri,
        String localName,
        String qName,
        Attributes attributes) throws SAXException {
      if ("project".equals(qName)) { //$NON-NLS-1$
        currentProject = new Project(
            attributes.getValue("name"), //$NON-NLS-1$
            attributes.getValue("path"), //$NON-NLS-1$
            attributes.getValue("revision"), //$NON-NLS-1$
            attributes.getValue("remote"), //$NON-NLS-1$
            attributes.getValue("groups")); //$NON-NLS-1$
      } else if ("remote".equals(qName)) { //$NON-NLS-1$
        String alias = attributes.getValue("alias"); //$NON-NLS-1$
        String fetch = attributes.getValue("fetch"); //$NON-NLS-1$
        remotes.put(attributes.getValue("name"), fetch); //$NON-NLS-1$
        if (alias != null)
          remotes.put(alias, fetch);
      } else if ("default".equals(qName)) { //$NON-NLS-1$
        defaultRemote = attributes.getValue("remote"); //$NON-NLS-1$
        defaultRevision = attributes.getValue("revision"); //$NON-NLS-1$
        if (defaultRevision == null)
          defaultRevision = command.branch;
      } else if ("copyfile".equals(qName)) { //$NON-NLS-1$
        if (currentProject == null)
          throw new SAXException(RepoText.get().invalidManifest);
        currentProject.addCopyFile(new CopyFile(
              command.repo,
              currentProject.path,
              attributes.getValue("src"), //$NON-NLS-1$
              attributes.getValue("dest"))); //$NON-NLS-1$
      } else if ("include".equals(qName)) { //$NON_NLS-1$
        String name = attributes.getValue("name");
        InputStream is = null;
        if (includedReader != null) {
          try {
            is = includedReader.readIncludeFile(name);
          } catch (Exception e) {
            throw new SAXException(MessageFormat.format(
                RepoText.get().errorIncludeFile, name), e);
          }
        } else if (filename != null) {
          int index = filename.lastIndexOf('/');
          String path = filename.substring(0, index + 1) + name;
          try {
            is = new FileInputStream(path);
          } catch (IOException e) {
            throw new SAXException(MessageFormat.format(
                RepoText.get().errorIncludeFile, path), e);
          }
        }
        if (is == null) {
          throw new SAXException(
              RepoText.get().errorIncludeNotImplemented);
        }
        try {
          read(is);
        } catch (IOException e) {
          throw new SAXException(e);
        }
      }
    }

    @Override
    public void endElement(
        String uri,
        String localName,
        String qName) throws SAXException {
      if ("project".equals(qName)) { //$NON-NLS-1$
        projects.add(currentProject);
        currentProject = null;
      }
    }

    @Override
    public void endDocument() throws SAXException {
      xmlInRead--;
      if (xmlInRead != 0)
        return;

      // Only do the following after we finished reading everything.
      removeNotInGroup();
      removeOverlaps();

      Map<String, String> remoteUrls = new HashMap<String, String>();
      URI baseUri;
      try {
        baseUri = new URI(baseUrl);
      } catch (URISyntaxException e) {
        throw new SAXException(e);
      }
      for (Project proj : projects) {
        String remote = proj.remote;
        if (remote == null) {
          if (defaultRemote == null) {
            if (filename != null)
              throw new SAXException(MessageFormat.format(
                  RepoText.get().errorNoDefaultFilename,
                  filename));
            else
              throw new SAXException(
                  RepoText.get().errorNoDefault);
          }
          remote = defaultRemote;
        }
        String remoteUrl = remoteUrls.get(remote);
        if (remoteUrl == null) {
          remoteUrl = baseUri.resolve(remotes.get(remote)).toString();
          if (!remoteUrl.endsWith("/"))
            remoteUrl = remoteUrl + "/";
          remoteUrls.put(remote, remoteUrl);
        }

        command.addSubmodule(remoteUrl + proj.name,
            proj.path,
            proj.revision == null
                ? defaultRevision : proj.revision,
            proj.copyfiles);
      }
    }

    /** Remove projects that are not in our desired groups. */
    void removeNotInGroup() {
      Iterator<Project> iter = projects.iterator();
      while (iter.hasNext())
        if (!inGroups(iter.next()))
          iter.remove();
    }

    /** Remove projects that sits in a subdirectory of any other project. */
    void removeOverlaps() {
      Collections.sort(projects);
      Iterator<Project> iter = projects.iterator();
      if (!iter.hasNext())
        return;
      Project last = iter.next();
      while (iter.hasNext()) {
        Project p = iter.next();
        if (last.isAncestorOf(p))
          iter.remove();
        else
          last = p;
      }
    }

    boolean inGroups(Project proj) {
      for (String group : minusGroups) {
        if (proj.groups.contains(group)) {
          // minus groups have highest priority.
          return false;
        }
      }
      if (plusGroups.isEmpty() || plusGroups.contains("all")) { //$NON-NLS-1$
        // empty plus groups means "all"
        return true;
      }
      for (String group : plusGroups) {
        if (proj.groups.contains(group))
          return true;
      }
      return false;
    }
  }

  @SuppressWarnings("serial")
  private static class ManifestErrorException extends GitAPIException {
    ManifestErrorException(Throwable cause) {
      super(RepoText.get().invalidManifest, cause);
    }
  }

  @SuppressWarnings("serial")
  private static class RemoteUnavailableException extends GitAPIException {
    RemoteUnavailableException(String uri) {
      super(MessageFormat.format(RepoText.get().errorRemoteUnavailable, uri));
    }
  }

  /**
   * @param repo
   */
  public RepoCommand(final Repository repo) {
    super(repo);
  }

  /**
   * Set path to the manifest XML file.
   *
   * Calling {@link #setInputStream} will ignore the path set here.
   *
   * @param path
   *            (with <code>/</code> as separator)
   * @return this command
   */
  public RepoCommand setPath(final String path) {
    this.path = path;
    return this;
  }

  /**
   * Set the input stream to the manifest XML.
   *
   * Setting inputStream will ignore the path set. It will be closed in
   * {@link #call}.
   *
   * @param inputStream
   * @return this command
   * @since 3.5
   */
  public RepoCommand setInputStream(final InputStream inputStream) {
    this.inputStream = inputStream;
    return this;
  }

  /**
   * Set base URI of the pathes inside the XML
   *
   * @param uri
   * @return this command
   */
  public RepoCommand setURI(final String uri) {
    this.uri = uri;
    return this;
  }

  /**
   * Set groups to sync
   *
   * @param groups groups separated by comma, examples: default|all|G1,-G2,-G3
   * @return this command
   */
  public RepoCommand setGroups(final String groups) {
    this.groups = groups;
    return this;
  }

  /**
   * Set default branch.
   *
   * This is generally the name of the branch the manifest file was in. If
   * there's no default revision (branch) specified in manifest and no
   * revision specified in project, this branch will be used.
   *
   * @param branch
   * @return this command
   */
  public RepoCommand setBranch(final String branch) {
    this.branch = branch;
    return this;
  }

  /**
   * The progress monitor associated with the clone operation. By default,
   * this is set to <code>NullProgressMonitor</code>
   *
   * @see org.eclipse.jgit.lib.NullProgressMonitor
   * @param monitor
   * @return this command
   */
  public RepoCommand setProgressMonitor(final ProgressMonitor monitor) {
    this.monitor = monitor;
    return this;
  }

  /**
   * Set the author/committer for the bare repository commit.
   *
   * For non-bare repositories, the current user will be used and this will be
   * ignored.
   *
   * @param author
   * @return this command
   */
  public RepoCommand setAuthor(final PersonIdent author) {
    this.author = author;
    return this;
  }

  /**
   * Set the GetHeadFromUri callback.
   *
   * This is only used in bare repositories.
   *
   * @param callback
   * @return this command
   */
  public RepoCommand setRemoteReader(final RemoteReader callback) {
    this.callback = callback;
    return this;
  }

  /**
   * Set the IncludedFileReader callback.
   *
   * @param reader
   * @return this command
   * @since 3.5
   */
  public RepoCommand setIncludedFileReader(IncludedFileReader reader) {
    this.includedReader = reader;
    return this;
  }

  @Override
  public RevCommit call() throws GitAPIException {
    try {
      checkCallable();
      if (uri == null || uri.length() == 0)
        throw new IllegalArgumentException(
            JGitText.get().uriNotConfigured);
      if (inputStream == null) {
        if (path == null || path.length() == 0)
          throw new IllegalArgumentException(
              JGitText.get().pathNotConfigured);
        try {
          inputStream = new FileInputStream(path);
        } catch (IOException e) {
          throw new IllegalArgumentException(
              JGitText.get().pathNotConfigured);
        }
      }

      if (repo.isBare()) {
        bareProjects = new ArrayList<Project>();
        if (author == null)
          author = new PersonIdent(repo);
        if (callback == null)
          callback = new DefaultRemoteReader();
      } else
        git = new Git(repo);

      XmlManifest manifest = new XmlManifest(
          this, includedReader, path, uri, groups);
      try {
        manifest.read(inputStream);
      } catch (IOException e) {
        throw new ManifestErrorException(e);
      }
    } finally {
      try {
        if (inputStream != null)
          inputStream.close();
      } catch (IOException e) {
        // Just ignore it, it's not important.
      }
    }

    if (repo.isBare()) {
      DirCache index = DirCache.newInCore();
      DirCacheBuilder builder = index.builder();
      ObjectInserter inserter = repo.newObjectInserter();
      RevWalk rw = new RevWalk(repo);
      try {
        Config cfg = new Config();
        for (Project proj : bareProjects) {
          String name = proj.path;
          String nameUri = proj.name;
          cfg.setString("submodule", name, "path", name); //$NON-NLS-1$ //$NON-NLS-2$
          cfg.setString("submodule", name, "url", nameUri); //$NON-NLS-1$ //$NON-NLS-2$
          // create gitlink
          DirCacheEntry dcEntry = new DirCacheEntry(name);
          ObjectId objectId;
          if (ObjectId.isId(proj.revision))
            objectId = ObjectId.fromString(proj.revision);
          else {
            objectId = callback.sha1(nameUri, proj.revision);
          }
          if (objectId == null)
            throw new RemoteUnavailableException(nameUri);
          dcEntry.setObjectId(objectId);
          dcEntry.setFileMode(FileMode.GITLINK);
          builder.add(dcEntry);

          for (CopyFile copyfile : proj.copyfiles) {
            byte[] src = callback.readFile(
                nameUri, proj.revision, copyfile.src);
            objectId = inserter.insert(Constants.OBJ_BLOB, src);
            dcEntry = new DirCacheEntry(copyfile.dest);
            dcEntry.setObjectId(objectId);
            dcEntry.setFileMode(FileMode.REGULAR_FILE);
            builder.add(dcEntry);
          }
        }
        String content = cfg.toText();

        // create a new DirCacheEntry for .gitmodules file.
        final DirCacheEntry dcEntry = new DirCacheEntry(Constants.DOT_GIT_MODULES);
        ObjectId objectId = inserter.insert(Constants.OBJ_BLOB,
            content.getBytes(Constants.CHARACTER_ENCODING));
        dcEntry.setObjectId(objectId);
        dcEntry.setFileMode(FileMode.REGULAR_FILE);
        builder.add(dcEntry);

        builder.finish();
        ObjectId treeId = index.writeTree(inserter);

        // Create a Commit object, populate it and write it
        ObjectId headId = repo.resolve(Constants.HEAD + "^{commit}"); //$NON-NLS-1$
        CommitBuilder commit = new CommitBuilder();
        commit.setTreeId(treeId);
        if (headId != null)
          commit.setParentIds(headId);
        commit.setAuthor(author);
        commit.setCommitter(author);
        commit.setMessage(RepoText.get().repoCommitMessage);

        ObjectId commitId = inserter.insert(commit);
        inserter.flush();

        RefUpdate ru = repo.updateRef(Constants.HEAD);
        ru.setNewObjectId(commitId);
        ru.setExpectedOldObjectId(headId != null ? headId : ObjectId.zeroId());
        Result rc = ru.update(rw);

        switch (rc) {
          case NEW:
          case FORCED:
          case FAST_FORWARD:
            // Successful. Do nothing.
            break;
          case REJECTED:
          case LOCK_FAILURE:
            throw new ConcurrentRefUpdateException(
                JGitText.get().couldNotLockHEAD, ru.getRef(),
                rc);
          default:
            throw new JGitInternalException(MessageFormat.format(
                JGitText.get().updatingRefFailed,
                Constants.HEAD, commitId.name(), rc));
        }

        return rw.parseCommit(commitId);
      } catch (IOException e) {
        throw new ManifestErrorException(e);
      } finally {
        rw.release();
      }
    } else {
      return git
        .commit()
        .setMessage(RepoText.get().repoCommitMessage)
        .call();
    }
  }

  private void addSubmodule(String url, String name, String revision,
      List<CopyFile> copyfiles) throws SAXException {
    if (repo.isBare()) {
      Project proj = new Project(url, name, revision, null, null);
      proj.copyfiles.addAll(copyfiles);
      bareProjects.add(proj);
    } else {
      SubmoduleAddCommand add = git
        .submoduleAdd()
        .setPath(name)
        .setURI(url);
      if (monitor != null)
        add.setProgressMonitor(monitor);

      try {
        Repository subRepo = add.call();
        if (revision != null) {
          Git sub = new Git(subRepo);
          sub.checkout().setName(findRef(revision, subRepo)).call();
          git.add().addFilepattern(name).call();
        }
        for (CopyFile copyfile : copyfiles) {
          copyfile.copy();
          git.add().addFilepattern(copyfile.dest).call();
        }
      } catch (GitAPIException e) {
        throw new SAXException(e);
      } catch (IOException e) {
        throw new SAXException(e);
      }
    }
  }

  private static String findRef(String ref, Repository repo)
      throws IOException {
    if (!ObjectId.isId(ref)) {
      Ref r = repo.getRef(Constants.DEFAULT_REMOTE_NAME + "/" + ref); //$NON-NLS-1$
      if (r != null)
        return r.getName();
    }
    return ref;
  }
}
TOP

Related Classes of org.eclipse.jgit.gitrepo.RepoCommand

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.