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.

StashApplyCommand.java 13KB

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 anni fa
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 anni fa
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 anni fa
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 anni fa
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 anni fa
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 anni fa
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /*
  2. * Copyright (C) 2012, 2017 GitHub Inc.
  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.IOException;
  45. import java.text.MessageFormat;
  46. import java.util.HashSet;
  47. import java.util.List;
  48. import java.util.Set;
  49. import org.eclipse.jgit.api.errors.GitAPIException;
  50. import org.eclipse.jgit.api.errors.InvalidRefNameException;
  51. import org.eclipse.jgit.api.errors.JGitInternalException;
  52. import org.eclipse.jgit.api.errors.NoHeadException;
  53. import org.eclipse.jgit.api.errors.StashApplyFailureException;
  54. import org.eclipse.jgit.api.errors.WrongRepositoryStateException;
  55. import org.eclipse.jgit.dircache.DirCache;
  56. import org.eclipse.jgit.dircache.DirCacheBuilder;
  57. import org.eclipse.jgit.dircache.DirCacheCheckout;
  58. import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
  59. import org.eclipse.jgit.dircache.DirCacheEntry;
  60. import org.eclipse.jgit.dircache.DirCacheIterator;
  61. import org.eclipse.jgit.errors.CheckoutConflictException;
  62. import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
  63. import org.eclipse.jgit.internal.JGitText;
  64. import org.eclipse.jgit.lib.Constants;
  65. import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
  66. import org.eclipse.jgit.lib.ObjectId;
  67. import org.eclipse.jgit.lib.ObjectReader;
  68. import org.eclipse.jgit.lib.Repository;
  69. import org.eclipse.jgit.lib.RepositoryState;
  70. import org.eclipse.jgit.merge.MergeStrategy;
  71. import org.eclipse.jgit.merge.ResolveMerger;
  72. import org.eclipse.jgit.revwalk.RevCommit;
  73. import org.eclipse.jgit.revwalk.RevTree;
  74. import org.eclipse.jgit.revwalk.RevWalk;
  75. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  76. import org.eclipse.jgit.treewalk.FileTreeIterator;
  77. import org.eclipse.jgit.treewalk.TreeWalk;
  78. /**
  79. * Command class to apply a stashed commit.
  80. *
  81. * This class behaves like <em>git stash apply --index</em>, i.e. it tries to
  82. * recover the stashed index state in addition to the working tree state.
  83. *
  84. * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-stash.html"
  85. * >Git documentation about Stash</a>
  86. *
  87. * @since 2.0
  88. */
  89. public class StashApplyCommand extends GitCommand<ObjectId> {
  90. private static final String DEFAULT_REF = Constants.STASH + "@{0}"; //$NON-NLS-1$
  91. private String stashRef;
  92. private boolean applyIndex = true;
  93. private boolean applyUntracked = true;
  94. private boolean ignoreRepositoryState;
  95. private MergeStrategy strategy = MergeStrategy.RECURSIVE;
  96. /**
  97. * Create command to apply the changes of a stashed commit
  98. *
  99. * @param repo
  100. */
  101. public StashApplyCommand(final Repository repo) {
  102. super(repo);
  103. }
  104. /**
  105. * Set the stash reference to apply
  106. * <p>
  107. * This will default to apply the latest stashed commit (stash@{0}) if
  108. * unspecified
  109. *
  110. * @param stashRef
  111. * @return {@code this}
  112. */
  113. public StashApplyCommand setStashRef(final String stashRef) {
  114. this.stashRef = stashRef;
  115. return this;
  116. }
  117. /**
  118. * @param willIgnoreRepositoryState
  119. * @return {@code this}
  120. * @since 3.2
  121. */
  122. public StashApplyCommand ignoreRepositoryState(boolean willIgnoreRepositoryState) {
  123. this.ignoreRepositoryState = willIgnoreRepositoryState;
  124. return this;
  125. }
  126. private ObjectId getStashId() throws GitAPIException {
  127. final String revision = stashRef != null ? stashRef : DEFAULT_REF;
  128. final ObjectId stashId;
  129. try {
  130. stashId = repo.resolve(revision);
  131. } catch (IOException e) {
  132. throw new InvalidRefNameException(MessageFormat.format(
  133. JGitText.get().stashResolveFailed, revision), e);
  134. }
  135. if (stashId == null)
  136. throw new InvalidRefNameException(MessageFormat.format(
  137. JGitText.get().stashResolveFailed, revision));
  138. return stashId;
  139. }
  140. /**
  141. * Apply the changes in a stashed commit to the working directory and index
  142. *
  143. * @return id of stashed commit that was applied TODO: Does anyone depend on
  144. * this, or could we make it more like Merge/CherryPick/Revert?
  145. * @throws GitAPIException
  146. * @throws WrongRepositoryStateException
  147. * @throws NoHeadException
  148. * @throws StashApplyFailureException
  149. */
  150. @Override
  151. public ObjectId call() throws GitAPIException,
  152. WrongRepositoryStateException, NoHeadException,
  153. StashApplyFailureException {
  154. checkCallable();
  155. if (!ignoreRepositoryState
  156. && repo.getRepositoryState() != RepositoryState.SAFE)
  157. throw new WrongRepositoryStateException(MessageFormat.format(
  158. JGitText.get().stashApplyOnUnsafeRepository,
  159. repo.getRepositoryState()));
  160. try (ObjectReader reader = repo.newObjectReader();
  161. RevWalk revWalk = new RevWalk(reader)) {
  162. ObjectId headCommit = repo.resolve(Constants.HEAD);
  163. if (headCommit == null)
  164. throw new NoHeadException(JGitText.get().stashApplyWithoutHead);
  165. final ObjectId stashId = getStashId();
  166. RevCommit stashCommit = revWalk.parseCommit(stashId);
  167. if (stashCommit.getParentCount() < 2
  168. || stashCommit.getParentCount() > 3)
  169. throw new JGitInternalException(MessageFormat.format(
  170. JGitText.get().stashCommitIncorrectNumberOfParents,
  171. stashId.name(),
  172. Integer.valueOf(stashCommit.getParentCount())));
  173. ObjectId headTree = repo.resolve(Constants.HEAD + "^{tree}"); //$NON-NLS-1$
  174. ObjectId stashIndexCommit = revWalk.parseCommit(stashCommit
  175. .getParent(1));
  176. ObjectId stashHeadCommit = stashCommit.getParent(0);
  177. ObjectId untrackedCommit = null;
  178. if (applyUntracked && stashCommit.getParentCount() == 3)
  179. untrackedCommit = revWalk.parseCommit(stashCommit.getParent(2));
  180. ResolveMerger merger = (ResolveMerger) strategy.newMerger(repo);
  181. merger.setCommitNames(new String[] { "stashed HEAD", "HEAD", //$NON-NLS-1$ //$NON-NLS-2$
  182. "stash" }); //$NON-NLS-1$
  183. merger.setBase(stashHeadCommit);
  184. merger.setWorkingTreeIterator(new FileTreeIterator(repo));
  185. boolean mergeSucceeded = merger.merge(headCommit, stashCommit);
  186. List<String> modifiedByMerge = merger.getModifiedFiles();
  187. if (!modifiedByMerge.isEmpty()) {
  188. repo.fireEvent(
  189. new WorkingTreeModifiedEvent(modifiedByMerge, null));
  190. }
  191. if (mergeSucceeded) {
  192. DirCache dc = repo.lockDirCache();
  193. DirCacheCheckout dco = new DirCacheCheckout(repo, headTree,
  194. dc, merger.getResultTreeId());
  195. dco.setFailOnConflict(true);
  196. dco.checkout(); // Ignoring failed deletes....
  197. if (applyIndex) {
  198. ResolveMerger ixMerger = (ResolveMerger) strategy
  199. .newMerger(repo, true);
  200. ixMerger.setCommitNames(new String[] { "stashed HEAD", //$NON-NLS-1$
  201. "HEAD", "stashed index" }); //$NON-NLS-1$//$NON-NLS-2$
  202. ixMerger.setBase(stashHeadCommit);
  203. boolean ok = ixMerger.merge(headCommit, stashIndexCommit);
  204. if (ok) {
  205. resetIndex(revWalk
  206. .parseTree(ixMerger.getResultTreeId()));
  207. } else {
  208. throw new StashApplyFailureException(
  209. JGitText.get().stashApplyConflict);
  210. }
  211. }
  212. if (untrackedCommit != null) {
  213. ResolveMerger untrackedMerger = (ResolveMerger) strategy
  214. .newMerger(repo, true);
  215. untrackedMerger.setCommitNames(new String[] {
  216. "null", "HEAD", "untracked files" }); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
  217. // There is no common base for HEAD & untracked files
  218. // because the commit for untracked files has no parent. If
  219. // we use stashHeadCommit as common base (as in the other
  220. // merges) we potentially report conflicts for files
  221. // which are not even member of untracked files commit
  222. untrackedMerger.setBase(null);
  223. boolean ok = untrackedMerger.merge(headCommit,
  224. untrackedCommit);
  225. if (ok) {
  226. try {
  227. RevTree untrackedTree = revWalk
  228. .parseTree(untrackedCommit);
  229. resetUntracked(untrackedTree);
  230. } catch (CheckoutConflictException e) {
  231. throw new StashApplyFailureException(
  232. JGitText.get().stashApplyConflict, e);
  233. }
  234. } else {
  235. throw new StashApplyFailureException(
  236. JGitText.get().stashApplyConflict);
  237. }
  238. }
  239. } else {
  240. throw new StashApplyFailureException(
  241. JGitText.get().stashApplyConflict);
  242. }
  243. return stashId;
  244. } catch (JGitInternalException e) {
  245. throw e;
  246. } catch (IOException e) {
  247. throw new JGitInternalException(JGitText.get().stashApplyFailed, e);
  248. }
  249. }
  250. /**
  251. * @param applyIndex
  252. * true (default) if the command should restore the index state
  253. */
  254. public void setApplyIndex(boolean applyIndex) {
  255. this.applyIndex = applyIndex;
  256. }
  257. /**
  258. * @param strategy
  259. * The merge strategy to use in order to merge during this
  260. * command execution.
  261. * @return {@code this}
  262. * @since 3.4
  263. */
  264. public StashApplyCommand setStrategy(MergeStrategy strategy) {
  265. this.strategy = strategy;
  266. return this;
  267. }
  268. /**
  269. * @param applyUntracked
  270. * true (default) if the command should restore untracked files
  271. * @since 3.4
  272. */
  273. public void setApplyUntracked(boolean applyUntracked) {
  274. this.applyUntracked = applyUntracked;
  275. }
  276. private void resetIndex(RevTree tree) throws IOException {
  277. DirCache dc = repo.lockDirCache();
  278. try (TreeWalk walk = new TreeWalk(repo)) {
  279. DirCacheBuilder builder = dc.builder();
  280. walk.addTree(tree);
  281. walk.addTree(new DirCacheIterator(dc));
  282. walk.setRecursive(true);
  283. while (walk.next()) {
  284. AbstractTreeIterator cIter = walk.getTree(0,
  285. AbstractTreeIterator.class);
  286. if (cIter == null) {
  287. // Not in commit, don't add to new index
  288. continue;
  289. }
  290. final DirCacheEntry entry = new DirCacheEntry(walk.getRawPath());
  291. entry.setFileMode(cIter.getEntryFileMode());
  292. entry.setObjectIdFromRaw(cIter.idBuffer(), cIter.idOffset());
  293. DirCacheIterator dcIter = walk.getTree(1,
  294. DirCacheIterator.class);
  295. if (dcIter != null && dcIter.idEqual(cIter)) {
  296. DirCacheEntry indexEntry = dcIter.getDirCacheEntry();
  297. entry.setLastModified(indexEntry.getLastModified());
  298. entry.setLength(indexEntry.getLength());
  299. }
  300. builder.add(entry);
  301. }
  302. builder.commit();
  303. } finally {
  304. dc.unlock();
  305. }
  306. }
  307. private void resetUntracked(RevTree tree) throws CheckoutConflictException,
  308. IOException {
  309. Set<String> actuallyModifiedPaths = new HashSet<>();
  310. // TODO maybe NameConflictTreeWalk ?
  311. try (TreeWalk walk = new TreeWalk(repo)) {
  312. walk.addTree(tree);
  313. walk.addTree(new FileTreeIterator(repo));
  314. walk.setRecursive(true);
  315. final ObjectReader reader = walk.getObjectReader();
  316. while (walk.next()) {
  317. final AbstractTreeIterator cIter = walk.getTree(0,
  318. AbstractTreeIterator.class);
  319. if (cIter == null)
  320. // Not in commit, don't create untracked
  321. continue;
  322. final EolStreamType eolStreamType = walk.getEolStreamType();
  323. final DirCacheEntry entry = new DirCacheEntry(walk.getRawPath());
  324. entry.setFileMode(cIter.getEntryFileMode());
  325. entry.setObjectIdFromRaw(cIter.idBuffer(), cIter.idOffset());
  326. FileTreeIterator fIter = walk
  327. .getTree(1, FileTreeIterator.class);
  328. if (fIter != null) {
  329. if (fIter.isModified(entry, true, reader)) {
  330. // file exists and is dirty
  331. throw new CheckoutConflictException(
  332. entry.getPathString());
  333. }
  334. }
  335. checkoutPath(entry, reader,
  336. new CheckoutMetadata(eolStreamType, null));
  337. actuallyModifiedPaths.add(entry.getPathString());
  338. }
  339. } finally {
  340. if (!actuallyModifiedPaths.isEmpty()) {
  341. repo.fireEvent(new WorkingTreeModifiedEvent(
  342. actuallyModifiedPaths, null));
  343. }
  344. }
  345. }
  346. private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
  347. CheckoutMetadata checkoutMetadata) {
  348. try {
  349. DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
  350. checkoutMetadata);
  351. } catch (IOException e) {
  352. throw new JGitInternalException(MessageFormat.format(
  353. JGitText.get().checkoutConflictWithFile,
  354. entry.getPathString()), e);
  355. }
  356. }
  357. }