1 /* 2 * Copyright (C) 2010, 2012 Chris Aniszczyk <caniszczyk@gmail.com> 3 * and other copyright owners as documented in the project's IP log. 4 * 5 * This program and the accompanying materials are made available 6 * under the terms of the Eclipse Distribution License v1.0 which 7 * accompanies this distribution, is reproduced below, and is 8 * available at http://www.eclipse.org/org/documents/edl-v10.php 9 * 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 19 * - Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials provided 22 * with the distribution. 23 * 24 * - Neither the name of the Eclipse Foundation, Inc. nor the 25 * names of its contributors may be used to endorse or promote 26 * products derived from this software without specific prior 27 * written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 package org.eclipse.jgit.api; 44 45 import java.io.File; 46 import java.io.IOException; 47 import java.util.Collection; 48 import java.util.LinkedList; 49 50 import org.eclipse.jgit.api.errors.GitAPIException; 51 import org.eclipse.jgit.api.errors.JGitInternalException; 52 import org.eclipse.jgit.api.errors.NoFilepatternException; 53 import org.eclipse.jgit.dircache.DirCache; 54 import org.eclipse.jgit.dircache.DirCacheBuildIterator; 55 import org.eclipse.jgit.dircache.DirCacheBuilder; 56 import org.eclipse.jgit.internal.JGitText; 57 import org.eclipse.jgit.lib.Constants; 58 import org.eclipse.jgit.lib.FileMode; 59 import org.eclipse.jgit.lib.Repository; 60 import org.eclipse.jgit.treewalk.TreeWalk; 61 import org.eclipse.jgit.treewalk.filter.PathFilterGroup; 62 63 /** 64 * Remove files from the index and working directory (or optionally only from 65 * the index). 66 * <p> 67 * It has setters for all supported options and arguments of this command and a 68 * {@link #call()} method to finally execute the command. Each instance of this 69 * class should only be used for one invocation of the command (means: one call 70 * to {@link #call()}). 71 * <p> 72 * Examples (<code>git</code> is a {@link Git} instance): 73 * <p> 74 * Remove file "test.txt" from both index and working directory: 75 * 76 * <pre> 77 * git.rm().addFilepattern("test.txt").call(); 78 * </pre> 79 * <p> 80 * Remove file "new.txt" from the index (but not from the working directory): 81 * 82 * <pre> 83 * git.rm().setCached(true).addFilepattern("new.txt").call(); 84 * </pre> 85 * 86 * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-rm.html" 87 * >Git documentation about Rm</a> 88 */ 89 public class RmCommand extends GitCommand<DirCache> { 90 91 private Collection<String> filepatterns; 92 93 /** Only remove files from index, not from working directory */ 94 private boolean cached = false; 95 96 /** 97 * 98 * @param repo 99 */ 100 public RmCommand(Repository repo) { 101 super(repo); 102 filepatterns = new LinkedList<>(); 103 } 104 105 /** 106 * @param filepattern 107 * repository-relative path of file to remove (with 108 * <code>/</code> as separator) 109 * @return {@code this} 110 */ 111 public RmCommand addFilepattern(String filepattern) { 112 checkCallable(); 113 filepatterns.add(filepattern); 114 return this; 115 } 116 117 /** 118 * Only remove the specified files from the index. 119 * 120 * @param cached 121 * true if files should only be removed from index, false if 122 * files should also be deleted from the working directory 123 * @return {@code this} 124 * @since 2.2 125 */ 126 public RmCommand setCached(boolean cached) { 127 checkCallable(); 128 this.cached = cached; 129 return this; 130 } 131 132 /** 133 * Executes the {@code Rm} command. Each instance of this class should only 134 * be used for one invocation of the command. Don't call this method twice 135 * on an instance. 136 * 137 * @return the DirCache after Rm 138 */ 139 @Override 140 public DirCache call() throws GitAPIException, 141 NoFilepatternException { 142 143 if (filepatterns.isEmpty()) 144 throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired); 145 checkCallable(); 146 DirCache dc = null; 147 148 try (final TreeWalk tw = new TreeWalk(repo)) { 149 dc = repo.lockDirCache(); 150 DirCacheBuilder builder = dc.builder(); 151 tw.reset(); // drop the first empty tree, which we do not need here 152 tw.setRecursive(true); 153 tw.setFilter(PathFilterGroup.createFromStrings(filepatterns)); 154 tw.addTree(new DirCacheBuildIterator(builder)); 155 156 while (tw.next()) { 157 if (!cached) { 158 final FileMode mode = tw.getFileMode(0); 159 if (mode.getObjectType() == Constants.OBJ_BLOB) { 160 final File path = new File(repo.getWorkTree(), 161 tw.getPathString()); 162 // Deleting a blob is simply a matter of removing 163 // the file or symlink named by the tree entry. 164 delete(path); 165 } 166 } 167 } 168 builder.commit(); 169 setCallable(false); 170 } catch (IOException e) { 171 throw new JGitInternalException( 172 JGitText.get().exceptionCaughtDuringExecutionOfRmCommand, e); 173 } finally { 174 if (dc != null) 175 dc.unlock(); 176 } 177 178 return dc; 179 } 180 181 private void delete(File p) { 182 while (p != null && !p.equals(repo.getWorkTree()) && p.delete()) 183 p = p.getParentFile(); 184 } 185 186 }