RmCommand.java

  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. import java.io.File;
  45. import java.io.IOException;
  46. import java.util.ArrayList;
  47. import java.util.Collection;
  48. import java.util.LinkedList;
  49. import java.util.List;

  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.events.WorkingTreeModifiedEvent;
  57. import org.eclipse.jgit.internal.JGitText;
  58. import org.eclipse.jgit.lib.Constants;
  59. import org.eclipse.jgit.lib.FileMode;
  60. import org.eclipse.jgit.lib.Repository;
  61. import org.eclipse.jgit.treewalk.TreeWalk;
  62. import org.eclipse.jgit.treewalk.filter.PathFilterGroup;

  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 org.eclipse.jgit.api.Git} instance):
  73.  * <p>
  74.  * Remove file "test.txt" from both index and working directory:
  75.  *
  76.  * <pre>
  77.  * git.rm().addFilepattern(&quot;test.txt&quot;).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(&quot;new.txt&quot;).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.     private Collection<String> filepatterns;

  91.     /** Only remove files from index, not from working directory */
  92.     private boolean cached = false;

  93.     /**
  94.      * Constructor for RmCommand.
  95.      *
  96.      * @param repo
  97.      *            the {@link org.eclipse.jgit.lib.Repository}
  98.      */
  99.     public RmCommand(Repository repo) {
  100.         super(repo);
  101.         filepatterns = new LinkedList<>();
  102.     }

  103.     /**
  104.      * Add file name pattern of files to be removed
  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.      * Only remove the specified files from the index.
  118.      *
  119.      * @param cached
  120.      *            {@code true} if files should only be removed from index,
  121.      *            {@code false} if files should also be deleted from the working
  122.      *            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.      * {@inheritDoc}
  133.      * <p>
  134.      * Executes the {@code Rm} command. Each instance of this class should only
  135.      * be used for one invocation of the command. Don't call this method twice
  136.      * on an instance.
  137.      */
  138.     @Override
  139.     public DirCache call() throws GitAPIException,
  140.             NoFilepatternException {

  141.         if (filepatterns.isEmpty())
  142.             throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
  143.         checkCallable();
  144.         DirCache dc = null;

  145.         List<String> actuallyDeletedFiles = new ArrayList<>();
  146.         try (TreeWalk tw = new TreeWalk(repo)) {
  147.             dc = repo.lockDirCache();
  148.             DirCacheBuilder builder = dc.builder();
  149.             tw.reset(); // drop the first empty tree, which we do not need here
  150.             tw.setRecursive(true);
  151.             tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
  152.             tw.addTree(new DirCacheBuildIterator(builder));

  153.             while (tw.next()) {
  154.                 if (!cached) {
  155.                     final FileMode mode = tw.getFileMode(0);
  156.                     if (mode.getObjectType() == Constants.OBJ_BLOB) {
  157.                         String relativePath = tw.getPathString();
  158.                         final File path = new File(repo.getWorkTree(),
  159.                                 relativePath);
  160.                         // Deleting a blob is simply a matter of removing
  161.                         // the file or symlink named by the tree entry.
  162.                         if (delete(path)) {
  163.                             actuallyDeletedFiles.add(relativePath);
  164.                         }
  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.             try {
  175.                 if (dc != null) {
  176.                     dc.unlock();
  177.                 }
  178.             } finally {
  179.                 if (!actuallyDeletedFiles.isEmpty()) {
  180.                     repo.fireEvent(new WorkingTreeModifiedEvent(null,
  181.                             actuallyDeletedFiles));
  182.                 }
  183.             }
  184.         }

  185.         return dc;
  186.     }

  187.     private boolean delete(File p) {
  188.         boolean deleted = false;
  189.         while (p != null && !p.equals(repo.getWorkTree()) && p.delete()) {
  190.             deleted = true;
  191.             p = p.getParentFile();
  192.         }
  193.         return deleted;
  194.     }

  195. }