RmCommand.java

  1. /*
  2.  * Copyright (C) 2010, 2012 Chris Aniszczyk <caniszczyk@gmail.com> and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */
  10. package org.eclipse.jgit.api;

  11. import java.io.File;
  12. import java.io.IOException;
  13. import java.util.ArrayList;
  14. import java.util.Collection;
  15. import java.util.LinkedList;
  16. import java.util.List;

  17. import org.eclipse.jgit.api.errors.GitAPIException;
  18. import org.eclipse.jgit.api.errors.JGitInternalException;
  19. import org.eclipse.jgit.api.errors.NoFilepatternException;
  20. import org.eclipse.jgit.dircache.DirCache;
  21. import org.eclipse.jgit.dircache.DirCacheBuildIterator;
  22. import org.eclipse.jgit.dircache.DirCacheBuilder;
  23. import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
  24. import org.eclipse.jgit.internal.JGitText;
  25. import org.eclipse.jgit.lib.Constants;
  26. import org.eclipse.jgit.lib.FileMode;
  27. import org.eclipse.jgit.lib.Repository;
  28. import org.eclipse.jgit.treewalk.TreeWalk;
  29. import org.eclipse.jgit.treewalk.filter.PathFilterGroup;

  30. /**
  31.  * Remove files from the index and working directory (or optionally only from
  32.  * the index).
  33.  * <p>
  34.  * It has setters for all supported options and arguments of this command and a
  35.  * {@link #call()} method to finally execute the command. Each instance of this
  36.  * class should only be used for one invocation of the command (means: one call
  37.  * to {@link #call()}).
  38.  * <p>
  39.  * Examples (<code>git</code> is a {@link org.eclipse.jgit.api.Git} instance):
  40.  * <p>
  41.  * Remove file "test.txt" from both index and working directory:
  42.  *
  43.  * <pre>
  44.  * git.rm().addFilepattern(&quot;test.txt&quot;).call();
  45.  * </pre>
  46.  * <p>
  47.  * Remove file "new.txt" from the index (but not from the working directory):
  48.  *
  49.  * <pre>
  50.  * git.rm().setCached(true).addFilepattern(&quot;new.txt&quot;).call();
  51.  * </pre>
  52.  *
  53.  * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-rm.html"
  54.  *      >Git documentation about Rm</a>
  55.  */
  56. public class RmCommand extends GitCommand<DirCache> {

  57.     private Collection<String> filepatterns;

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

  60.     /**
  61.      * Constructor for RmCommand.
  62.      *
  63.      * @param repo
  64.      *            the {@link org.eclipse.jgit.lib.Repository}
  65.      */
  66.     public RmCommand(Repository repo) {
  67.         super(repo);
  68.         filepatterns = new LinkedList<>();
  69.     }

  70.     /**
  71.      * Add file name pattern of files to be removed
  72.      *
  73.      * @param filepattern
  74.      *            repository-relative path of file to remove (with
  75.      *            <code>/</code> as separator)
  76.      * @return {@code this}
  77.      */
  78.     public RmCommand addFilepattern(String filepattern) {
  79.         checkCallable();
  80.         filepatterns.add(filepattern);
  81.         return this;
  82.     }

  83.     /**
  84.      * Only remove the specified files from the index.
  85.      *
  86.      * @param cached
  87.      *            {@code true} if files should only be removed from index,
  88.      *            {@code false} if files should also be deleted from the working
  89.      *            directory
  90.      * @return {@code this}
  91.      * @since 2.2
  92.      */
  93.     public RmCommand setCached(boolean cached) {
  94.         checkCallable();
  95.         this.cached = cached;
  96.         return this;
  97.     }

  98.     /**
  99.      * {@inheritDoc}
  100.      * <p>
  101.      * Executes the {@code Rm} command. Each instance of this class should only
  102.      * be used for one invocation of the command. Don't call this method twice
  103.      * on an instance.
  104.      */
  105.     @Override
  106.     public DirCache call() throws GitAPIException,
  107.             NoFilepatternException {

  108.         if (filepatterns.isEmpty())
  109.             throw new NoFilepatternException(JGitText.get().atLeastOnePatternIsRequired);
  110.         checkCallable();
  111.         DirCache dc = null;

  112.         List<String> actuallyDeletedFiles = new ArrayList<>();
  113.         try (TreeWalk tw = new TreeWalk(repo)) {
  114.             dc = repo.lockDirCache();
  115.             DirCacheBuilder builder = dc.builder();
  116.             tw.reset(); // drop the first empty tree, which we do not need here
  117.             tw.setRecursive(true);
  118.             tw.setFilter(PathFilterGroup.createFromStrings(filepatterns));
  119.             tw.addTree(new DirCacheBuildIterator(builder));

  120.             while (tw.next()) {
  121.                 if (!cached) {
  122.                     final FileMode mode = tw.getFileMode(0);
  123.                     if (mode.getObjectType() == Constants.OBJ_BLOB) {
  124.                         String relativePath = tw.getPathString();
  125.                         final File path = new File(repo.getWorkTree(),
  126.                                 relativePath);
  127.                         // Deleting a blob is simply a matter of removing
  128.                         // the file or symlink named by the tree entry.
  129.                         if (delete(path)) {
  130.                             actuallyDeletedFiles.add(relativePath);
  131.                         }
  132.                     }
  133.                 }
  134.             }
  135.             builder.commit();
  136.             setCallable(false);
  137.         } catch (IOException e) {
  138.             throw new JGitInternalException(
  139.                     JGitText.get().exceptionCaughtDuringExecutionOfRmCommand, e);
  140.         } finally {
  141.             try {
  142.                 if (dc != null) {
  143.                     dc.unlock();
  144.                 }
  145.             } finally {
  146.                 if (!actuallyDeletedFiles.isEmpty()) {
  147.                     repo.fireEvent(new WorkingTreeModifiedEvent(null,
  148.                             actuallyDeletedFiles));
  149.                 }
  150.             }
  151.         }

  152.         return dc;
  153.     }

  154.     private boolean delete(File p) {
  155.         boolean deleted = false;
  156.         while (p != null && !p.equals(repo.getWorkTree()) && p.delete()) {
  157.             deleted = true;
  158.             p = p.getParentFile();
  159.         }
  160.         return deleted;
  161.     }

  162. }