Java tutorial
/* * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com> * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com> * 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.oecd.ant.git.custom; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.LinkedList; import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.api.errors.NoFilepatternException; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheBuildIterator; import org.eclipse.jgit.dircache.DirCacheBuilder; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.internal.JGitText; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.treewalk.FileTreeIterator; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.WorkingTreeIterator; import org.eclipse.jgit.treewalk.filter.PathFilterGroup; /** * A class used to execute a {@code Add} command. It has setters for all supported options and arguments of this command and a {@link #call()} method to finally * execute the command. Each instance of this class should only be used for one invocation of the command (means: one call to {@link #call()}) * * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-add.html" >Git documentation about Add</a> */ public class CustomAddCommand extends GitCommand<DirCache> { private final Collection<String> filepatterns; private WorkingTreeIterator workingTreeIterator; private boolean update = false; private boolean all = false; /** * * @param repo */ public CustomAddCommand(Repository repo) { super(repo); filepatterns = new LinkedList<String>(); } /** * Add a path to a file/directory whose content should be added. * <p> * A directory name (e.g. <code>dir</code> to add <code>dir/file1</code> and <code>dir/file2</code>) can also be given to add all files in the directory, * recursively. Fileglobs (e.g. *.c) are not yet supported. * * @param filepattern * repository-relative path of file/directory to add (with <code>/</code> as separator) * @return {@code this} */ public CustomAddCommand addFilepattern(String filepattern) { checkCallable(); filepatterns.add(filepattern); return this; } /** * Allow clients to provide their own implementation of a FileTreeIterator * * @param f * @return {@code this} */ public CustomAddCommand setWorkingTreeIterator(WorkingTreeIterator f) { workingTreeIterator = f; return this; } /** * Executes the {@code Add} command. Each instance of this class should only be used for one invocation of the command. Don't call this method twice on an * instance. * * @return the DirCache after Add */ @Override public DirCache call() throws GitAPIException, NoFilepatternException { if (filepatterns.isEmpty()) throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired); checkCallable(); DirCache dc = null; boolean addAll = false; if (filepatterns.contains(".")) //$NON-NLS-1$ addAll = true; try (ObjectInserter inserter = repo.newObjectInserter(); final TreeWalk tw = new TreeWalk(repo)) { dc = repo.lockDirCache(); DirCacheIterator c; DirCacheBuilder builder = dc.builder(); tw.addTree(new DirCacheBuildIterator(builder)); if (workingTreeIterator == null) workingTreeIterator = new FileTreeIterator(repo); tw.addTree(workingTreeIterator); tw.setRecursive(true); if (!addAll) tw.setFilter(PathFilterGroup.createFromStrings(filepatterns)); String lastAddedFile = null; while (tw.next()) { String path = tw.getPathString(); WorkingTreeIterator f = tw.getTree(1, WorkingTreeIterator.class); if (tw.getTree(0, DirCacheIterator.class) == null && f != null && f.isEntryIgnored()) { // file is not in index but is ignored, do nothing } // In case of an existing merge conflict the // DirCacheBuildIterator iterates over all stages of // this path, we however want to add only one // new DirCacheEntry per path. else if (!(path.equals(lastAddedFile))) { if (all || !(update && tw.getTree(0, DirCacheIterator.class) == null)) { c = tw.getTree(0, DirCacheIterator.class); if (f != null) { // the file exists long sz = f.getEntryLength(); DirCacheEntry entry = new DirCacheEntry(path); if (c == null || c.getDirCacheEntry() == null || !c.getDirCacheEntry().isAssumeValid()) { FileMode mode = f.getIndexFileMode(c); entry.setFileMode(mode); if (FileMode.GITLINK != mode) { entry.setLength(sz); entry.setLastModified(f.getEntryLastModified()); long contentSize = f.getEntryContentLength(); InputStream in = f.openEntryStream(); try { entry.setObjectId(inserter.insert(Constants.OBJ_BLOB, contentSize, in)); } finally { in.close(); } } else entry.setObjectId(f.getEntryObjectId()); builder.add(entry); lastAddedFile = path; } else { builder.add(c.getDirCacheEntry()); } } else if (c != null && (!(all || update) || FileMode.GITLINK == c.getEntryFileMode())) builder.add(c.getDirCacheEntry()); } } } inserter.flush(); builder.commit(); setCallable(false); } catch (IOException e) { throw new JGitInternalException(JGitText.get().exceptionCaughtDuringExecutionOfAddCommand, e); } finally { if (dc != null) dc.unlock(); } return dc; } /** * @param update * If set to true, the command only matches {@code filepattern} against already tracked files in the index rather than the working tree. That * means that it will never stage new files, but that it will stage modified new contents of tracked files and that it will remove files from the * index if the corresponding files in the working tree have been removed. In contrast to the git command line a {@code filepattern} must exist * also if update is set to true as there is no concept of a working directory here. * * @return {@code this} */ public CustomAddCommand setUpdate(boolean update) { this.update = update; return this; } /** * @return is the parameter update is set */ public boolean isUpdate() { return update; } /** * @param all * If set to true, the command update the index not only where the working tree has a file matching {@code filepattern} but also where the index * already has an entry. This adds, modifies, and removes index entries to match the working tree. * * @return {@code this} */ public CustomAddCommand setAll(boolean all) { this.all = all; return this; } /** * @return is the parameter all is set */ public boolean isAll() { return all; } }