You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

CleanCommand.java 8.5KB

Send a detailed event on working tree modifications Currently there is no way to determine the precise changes done to the working tree by a JGit command. Only the CheckoutCommand actually provides access to the lists of modified, deleted, and to-be-deleted files, but those lists may be inaccurate (since they are determined up-front before the working tree is modified) if the actual checkout then fails halfway through. Moreover, other JGit commands that modify the working tree do not offer any way to figure out which files were changed. This poses problems for EGit, which may need to refresh parts of the Eclipse workspace when JGit has done java.io file operations. Provide the foundations for better file change tracking: the working tree is modified exclusively in DirCacheCheckout. Make it emit a new type of RepositoryEvent that lists all files that were modified or deleted, even if the checkout failed halfway through. We update the 'updated' and 'removed' lists determined up-front in case of file system problems to reflect the actual state of changes made. EGit thus can register a listener for these events and then knows exactly which parts of the Eclipse workspace may need to be refreshed. Two commands manage checking out individual DirCacheEntries themselves: checkout specific paths, and applying a stash with untracked files. Make those two also emit such a new WorkingTreeModifiedEvent. Furthermore, merges may modify files, and clean, rm, and stash create may delete files. CQ: 13969 Bug: 500106 Change-Id: I7a100aee315791fa1201f43bbad61fbae60b35cb Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
7 lat temu
Send a detailed event on working tree modifications Currently there is no way to determine the precise changes done to the working tree by a JGit command. Only the CheckoutCommand actually provides access to the lists of modified, deleted, and to-be-deleted files, but those lists may be inaccurate (since they are determined up-front before the working tree is modified) if the actual checkout then fails halfway through. Moreover, other JGit commands that modify the working tree do not offer any way to figure out which files were changed. This poses problems for EGit, which may need to refresh parts of the Eclipse workspace when JGit has done java.io file operations. Provide the foundations for better file change tracking: the working tree is modified exclusively in DirCacheCheckout. Make it emit a new type of RepositoryEvent that lists all files that were modified or deleted, even if the checkout failed halfway through. We update the 'updated' and 'removed' lists determined up-front in case of file system problems to reflect the actual state of changes made. EGit thus can register a listener for these events and then knows exactly which parts of the Eclipse workspace may need to be refreshed. Two commands manage checking out individual DirCacheEntries themselves: checkout specific paths, and applying a stash with untracked files. Make those two also emit such a new WorkingTreeModifiedEvent. Furthermore, merges may modify files, and clean, rm, and stash create may delete files. CQ: 13969 Bug: 500106 Change-Id: I7a100aee315791fa1201f43bbad61fbae60b35cb Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
7 lat temu
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /*
  2. * Copyright (C) 2011, Chris Aniszczyk <zx@redhat.com>
  3. * Copyright (C) 2011, Abhishek Bhatnagar <abhatnag@redhat.com>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.api;
  45. import static org.eclipse.jgit.lib.Constants.DOT_GIT;
  46. import java.io.File;
  47. import java.io.IOException;
  48. import java.util.Collections;
  49. import java.util.Set;
  50. import java.util.TreeSet;
  51. import org.eclipse.jgit.api.errors.GitAPIException;
  52. import org.eclipse.jgit.api.errors.JGitInternalException;
  53. import org.eclipse.jgit.errors.NoWorkTreeException;
  54. import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
  55. import org.eclipse.jgit.lib.Repository;
  56. import org.eclipse.jgit.util.FS;
  57. import org.eclipse.jgit.util.FileUtils;
  58. /**
  59. * Remove untracked files from the working tree
  60. *
  61. * @see <a
  62. * href="http://www.kernel.org/pub/software/scm/git/docs/git-clean.html"
  63. * >Git documentation about Clean</a>
  64. */
  65. public class CleanCommand extends GitCommand<Set<String>> {
  66. private Set<String> paths = Collections.emptySet();
  67. private boolean dryRun;
  68. private boolean directories;
  69. private boolean ignore = true;
  70. private boolean force = false;
  71. /**
  72. * @param repo
  73. */
  74. protected CleanCommand(Repository repo) {
  75. super(repo);
  76. }
  77. /**
  78. * Executes the {@code clean} command with all the options and parameters
  79. * collected by the setter methods of this class. Each instance of this
  80. * class should only be used for one invocation of the command (means: one
  81. * call to {@link #call()})
  82. *
  83. * @return a set of strings representing each file cleaned.
  84. * @throws GitAPIException
  85. * @throws NoWorkTreeException
  86. */
  87. @Override
  88. public Set<String> call() throws NoWorkTreeException, GitAPIException {
  89. Set<String> files = new TreeSet<>();
  90. try {
  91. StatusCommand command = new StatusCommand(repo);
  92. Status status = command.call();
  93. Set<String> untrackedAndIgnoredFiles = new TreeSet<>(
  94. status.getUntracked());
  95. Set<String> untrackedAndIgnoredDirs = new TreeSet<>(
  96. status.getUntrackedFolders());
  97. FS fs = getRepository().getFS();
  98. for (String p : status.getIgnoredNotInIndex()) {
  99. File f = new File(repo.getWorkTree(), p);
  100. if (fs.isFile(f) || fs.isSymLink(f))
  101. untrackedAndIgnoredFiles.add(p);
  102. else if (fs.isDirectory(f))
  103. untrackedAndIgnoredDirs.add(p);
  104. }
  105. Set<String> filtered = filterFolders(untrackedAndIgnoredFiles,
  106. untrackedAndIgnoredDirs);
  107. Set<String> notIgnoredFiles = filterIgnorePaths(filtered,
  108. status.getIgnoredNotInIndex(), true);
  109. Set<String> notIgnoredDirs = filterIgnorePaths(
  110. untrackedAndIgnoredDirs,
  111. status.getIgnoredNotInIndex(), false);
  112. for (String file : notIgnoredFiles)
  113. if (paths.isEmpty() || paths.contains(file)) {
  114. files = cleanPath(file, files);
  115. }
  116. for (String dir : notIgnoredDirs)
  117. if (paths.isEmpty() || paths.contains(dir)) {
  118. files = cleanPath(dir, files);
  119. }
  120. } catch (IOException e) {
  121. throw new JGitInternalException(e.getMessage(), e);
  122. } finally {
  123. if (!files.isEmpty()) {
  124. repo.fireEvent(new WorkingTreeModifiedEvent(null, files));
  125. }
  126. }
  127. return files;
  128. }
  129. /**
  130. * When dryRun is false, deletes the specified path from disk. If dryRun
  131. * is true, no paths are actually deleted. In both cases, the paths that
  132. * would have been deleted are added to inFiles and returned.
  133. *
  134. * Paths that are directories are recursively deleted when
  135. * {@link #directories} is true.
  136. * Paths that are git repositories are recursively deleted when
  137. * {@link #directories} and {@link #force} are both true.
  138. *
  139. * @param path
  140. * The path to be cleaned
  141. * @param inFiles
  142. * A set of strings representing the files that have been cleaned
  143. * already, the path to be cleaned will be added to this set
  144. * before being returned.
  145. *
  146. * @return a set of strings with the cleaned path added to it
  147. * @throws IOException
  148. */
  149. private Set<String> cleanPath(String path, Set<String> inFiles)
  150. throws IOException {
  151. File curFile = new File(repo.getWorkTree(), path);
  152. if (curFile.isDirectory()) {
  153. if (directories) {
  154. // Is this directory a git repository?
  155. if (new File(curFile, DOT_GIT).exists()) {
  156. if (force) {
  157. if (!dryRun) {
  158. FileUtils.delete(curFile, FileUtils.RECURSIVE);
  159. }
  160. inFiles.add(path + "/"); //$NON-NLS-1$
  161. }
  162. } else {
  163. if (!dryRun) {
  164. FileUtils.delete(curFile, FileUtils.RECURSIVE);
  165. }
  166. inFiles.add(path + "/"); //$NON-NLS-1$
  167. }
  168. }
  169. } else {
  170. if (!dryRun) {
  171. FileUtils.delete(curFile, FileUtils.NONE);
  172. }
  173. inFiles.add(path);
  174. }
  175. return inFiles;
  176. }
  177. private Set<String> filterIgnorePaths(Set<String> inputPaths,
  178. Set<String> ignoredNotInIndex, boolean exact) {
  179. if (ignore) {
  180. Set<String> filtered = new TreeSet<>(inputPaths);
  181. for (String path : inputPaths)
  182. for (String ignored : ignoredNotInIndex)
  183. if ((exact && path.equals(ignored))
  184. || (!exact && path.startsWith(ignored))) {
  185. filtered.remove(path);
  186. break;
  187. }
  188. return filtered;
  189. }
  190. return inputPaths;
  191. }
  192. private Set<String> filterFolders(Set<String> untracked,
  193. Set<String> untrackedFolders) {
  194. Set<String> filtered = new TreeSet<>(untracked);
  195. for (String file : untracked)
  196. for (String folder : untrackedFolders)
  197. if (file.startsWith(folder)) {
  198. filtered.remove(file);
  199. break;
  200. }
  201. return filtered;
  202. }
  203. /**
  204. * If paths are set, only these paths are affected by the cleaning.
  205. *
  206. * @param paths
  207. * the paths to set (with <code>/</code> as separator)
  208. * @return {@code this}
  209. */
  210. public CleanCommand setPaths(Set<String> paths) {
  211. this.paths = paths;
  212. return this;
  213. }
  214. /**
  215. * If dryRun is set, the paths in question will not actually be deleted.
  216. *
  217. * @param dryRun
  218. * whether to do a dry run or not
  219. * @return {@code this}
  220. */
  221. public CleanCommand setDryRun(boolean dryRun) {
  222. this.dryRun = dryRun;
  223. return this;
  224. }
  225. /**
  226. * If force is set, directories that are git repositories will also be
  227. * deleted.
  228. *
  229. * @param force
  230. * whether or not to delete git repositories
  231. * @return {@code this}
  232. * @since 4.5
  233. */
  234. public CleanCommand setForce(boolean force) {
  235. this.force = force;
  236. return this;
  237. }
  238. /**
  239. * If dirs is set, in addition to files, also clean directories.
  240. *
  241. * @param dirs
  242. * whether to clean directories too, or only files.
  243. * @return {@code this}
  244. */
  245. public CleanCommand setCleanDirectories(boolean dirs) {
  246. directories = dirs;
  247. return this;
  248. }
  249. /**
  250. * If ignore is set, don't report/clean files/directories that are ignored
  251. * by a .gitignore. otherwise do handle them.
  252. *
  253. * @param ignore
  254. * whether to respect .gitignore or not.
  255. * @return {@code this}
  256. */
  257. public CleanCommand setIgnore(boolean ignore) {
  258. this.ignore = ignore;
  259. return this;
  260. }
  261. }