您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

StashCreateCommand.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>
6 年前
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>
6 年前
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>
6 年前
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>
6 年前
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>
6 年前
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>
6 年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. * Copyright (C) 2012, 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.File;
  45. import java.io.IOException;
  46. import java.io.InputStream;
  47. import java.text.MessageFormat;
  48. import java.util.ArrayList;
  49. import java.util.List;
  50. import org.eclipse.jgit.api.ResetCommand.ResetType;
  51. import org.eclipse.jgit.api.errors.GitAPIException;
  52. import org.eclipse.jgit.api.errors.JGitInternalException;
  53. import org.eclipse.jgit.api.errors.NoHeadException;
  54. import org.eclipse.jgit.api.errors.UnmergedPathsException;
  55. import org.eclipse.jgit.dircache.DirCache;
  56. import org.eclipse.jgit.dircache.DirCacheBuilder;
  57. import org.eclipse.jgit.dircache.DirCacheEditor;
  58. import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath;
  59. import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
  60. import org.eclipse.jgit.dircache.DirCacheEntry;
  61. import org.eclipse.jgit.dircache.DirCacheIterator;
  62. import org.eclipse.jgit.errors.UnmergedPathException;
  63. import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
  64. import org.eclipse.jgit.internal.JGitText;
  65. import org.eclipse.jgit.lib.CommitBuilder;
  66. import org.eclipse.jgit.lib.Constants;
  67. import org.eclipse.jgit.lib.MutableObjectId;
  68. import org.eclipse.jgit.lib.ObjectId;
  69. import org.eclipse.jgit.lib.ObjectInserter;
  70. import org.eclipse.jgit.lib.ObjectReader;
  71. import org.eclipse.jgit.lib.PersonIdent;
  72. import org.eclipse.jgit.lib.Ref;
  73. import org.eclipse.jgit.lib.RefUpdate;
  74. import org.eclipse.jgit.lib.Repository;
  75. import org.eclipse.jgit.revwalk.RevCommit;
  76. import org.eclipse.jgit.revwalk.RevWalk;
  77. import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  78. import org.eclipse.jgit.treewalk.FileTreeIterator;
  79. import org.eclipse.jgit.treewalk.TreeWalk;
  80. import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  81. import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
  82. import org.eclipse.jgit.treewalk.filter.IndexDiffFilter;
  83. import org.eclipse.jgit.treewalk.filter.SkipWorkTreeFilter;
  84. import org.eclipse.jgit.util.FileUtils;
  85. /**
  86. * Command class to stash changes in the working directory and index in a
  87. * commit.
  88. *
  89. * @see <a href="http://www.kernel.org/pub/software/scm/git/docs/git-stash.html"
  90. * >Git documentation about Stash</a>
  91. * @since 2.0
  92. */
  93. public class StashCreateCommand extends GitCommand<RevCommit> {
  94. private static final String MSG_INDEX = "index on {0}: {1} {2}"; //$NON-NLS-1$
  95. private static final String MSG_UNTRACKED = "untracked files on {0}: {1} {2}"; //$NON-NLS-1$
  96. private static final String MSG_WORKING_DIR = "WIP on {0}: {1} {2}"; //$NON-NLS-1$
  97. private String indexMessage = MSG_INDEX;
  98. private String workingDirectoryMessage = MSG_WORKING_DIR;
  99. private String ref = Constants.R_STASH;
  100. private PersonIdent person;
  101. private boolean includeUntracked;
  102. /**
  103. * Create a command to stash changes in the working directory and index
  104. *
  105. * @param repo
  106. * a {@link org.eclipse.jgit.lib.Repository} object.
  107. */
  108. public StashCreateCommand(Repository repo) {
  109. super(repo);
  110. person = new PersonIdent(repo);
  111. }
  112. /**
  113. * Set the message used when committing index changes
  114. * <p>
  115. * The message will be formatted with the current branch, abbreviated commit
  116. * id, and short commit message when used.
  117. *
  118. * @param message
  119. * the stash message
  120. * @return {@code this}
  121. */
  122. public StashCreateCommand setIndexMessage(String message) {
  123. indexMessage = message;
  124. return this;
  125. }
  126. /**
  127. * Set the message used when committing working directory changes
  128. * <p>
  129. * The message will be formatted with the current branch, abbreviated commit
  130. * id, and short commit message when used.
  131. *
  132. * @param message
  133. * the working directory message
  134. * @return {@code this}
  135. */
  136. public StashCreateCommand setWorkingDirectoryMessage(String message) {
  137. workingDirectoryMessage = message;
  138. return this;
  139. }
  140. /**
  141. * Set the person to use as the author and committer in the commits made
  142. *
  143. * @param person
  144. * the {@link org.eclipse.jgit.lib.PersonIdent} of the person who
  145. * creates the stash.
  146. * @return {@code this}
  147. */
  148. public StashCreateCommand setPerson(PersonIdent person) {
  149. this.person = person;
  150. return this;
  151. }
  152. /**
  153. * Set the reference to update with the stashed commit id If null, no
  154. * reference is updated
  155. * <p>
  156. * This value defaults to {@link org.eclipse.jgit.lib.Constants#R_STASH}
  157. *
  158. * @param ref
  159. * the name of the {@code Ref} to update
  160. * @return {@code this}
  161. */
  162. public StashCreateCommand setRef(String ref) {
  163. this.ref = ref;
  164. return this;
  165. }
  166. /**
  167. * Whether to include untracked files in the stash.
  168. *
  169. * @param includeUntracked
  170. * whether to include untracked files in the stash
  171. * @return {@code this}
  172. * @since 3.4
  173. */
  174. public StashCreateCommand setIncludeUntracked(boolean includeUntracked) {
  175. this.includeUntracked = includeUntracked;
  176. return this;
  177. }
  178. private RevCommit parseCommit(final ObjectReader reader,
  179. final ObjectId headId) throws IOException {
  180. try (RevWalk walk = new RevWalk(reader)) {
  181. return walk.parseCommit(headId);
  182. }
  183. }
  184. private CommitBuilder createBuilder() {
  185. CommitBuilder builder = new CommitBuilder();
  186. PersonIdent author = person;
  187. if (author == null)
  188. author = new PersonIdent(repo);
  189. builder.setAuthor(author);
  190. builder.setCommitter(author);
  191. return builder;
  192. }
  193. private void updateStashRef(ObjectId commitId, PersonIdent refLogIdent,
  194. String refLogMessage) throws IOException {
  195. if (ref == null)
  196. return;
  197. Ref currentRef = repo.findRef(ref);
  198. RefUpdate refUpdate = repo.updateRef(ref);
  199. refUpdate.setNewObjectId(commitId);
  200. refUpdate.setRefLogIdent(refLogIdent);
  201. refUpdate.setRefLogMessage(refLogMessage, false);
  202. refUpdate.setForceRefLog(true);
  203. if (currentRef != null)
  204. refUpdate.setExpectedOldObjectId(currentRef.getObjectId());
  205. else
  206. refUpdate.setExpectedOldObjectId(ObjectId.zeroId());
  207. refUpdate.forceUpdate();
  208. }
  209. private Ref getHead() throws GitAPIException {
  210. try {
  211. Ref head = repo.exactRef(Constants.HEAD);
  212. if (head == null || head.getObjectId() == null)
  213. throw new NoHeadException(JGitText.get().headRequiredToStash);
  214. return head;
  215. } catch (IOException e) {
  216. throw new JGitInternalException(JGitText.get().stashFailed, e);
  217. }
  218. }
  219. /**
  220. * {@inheritDoc}
  221. * <p>
  222. * Stash the contents on the working directory and index in separate commits
  223. * and reset to the current HEAD commit.
  224. */
  225. @Override
  226. public RevCommit call() throws GitAPIException {
  227. checkCallable();
  228. List<String> deletedFiles = new ArrayList<>();
  229. Ref head = getHead();
  230. try (ObjectReader reader = repo.newObjectReader()) {
  231. RevCommit headCommit = parseCommit(reader, head.getObjectId());
  232. DirCache cache = repo.lockDirCache();
  233. ObjectId commitId;
  234. try (ObjectInserter inserter = repo.newObjectInserter();
  235. TreeWalk treeWalk = new TreeWalk(repo, reader)) {
  236. treeWalk.setRecursive(true);
  237. treeWalk.addTree(headCommit.getTree());
  238. treeWalk.addTree(new DirCacheIterator(cache));
  239. treeWalk.addTree(new FileTreeIterator(repo));
  240. treeWalk.getTree(2, FileTreeIterator.class)
  241. .setDirCacheIterator(treeWalk, 1);
  242. treeWalk.setFilter(AndTreeFilter.create(new SkipWorkTreeFilter(
  243. 1), new IndexDiffFilter(1, 2)));
  244. // Return null if no local changes to stash
  245. if (!treeWalk.next())
  246. return null;
  247. MutableObjectId id = new MutableObjectId();
  248. List<PathEdit> wtEdits = new ArrayList<>();
  249. List<String> wtDeletes = new ArrayList<>();
  250. List<DirCacheEntry> untracked = new ArrayList<>();
  251. boolean hasChanges = false;
  252. do {
  253. AbstractTreeIterator headIter = treeWalk.getTree(0,
  254. AbstractTreeIterator.class);
  255. DirCacheIterator indexIter = treeWalk.getTree(1,
  256. DirCacheIterator.class);
  257. WorkingTreeIterator wtIter = treeWalk.getTree(2,
  258. WorkingTreeIterator.class);
  259. if (indexIter != null
  260. && !indexIter.getDirCacheEntry().isMerged())
  261. throw new UnmergedPathsException(
  262. new UnmergedPathException(
  263. indexIter.getDirCacheEntry()));
  264. if (wtIter != null) {
  265. if (indexIter == null && headIter == null
  266. && !includeUntracked)
  267. continue;
  268. hasChanges = true;
  269. if (indexIter != null && wtIter.idEqual(indexIter))
  270. continue;
  271. if (headIter != null && wtIter.idEqual(headIter))
  272. continue;
  273. treeWalk.getObjectId(id, 0);
  274. final DirCacheEntry entry = new DirCacheEntry(
  275. treeWalk.getRawPath());
  276. entry.setLength(wtIter.getEntryLength());
  277. entry.setLastModified(
  278. wtIter.getEntryLastModifiedInstant());
  279. entry.setFileMode(wtIter.getEntryFileMode());
  280. long contentLength = wtIter.getEntryContentLength();
  281. try (InputStream in = wtIter.openEntryStream()) {
  282. entry.setObjectId(inserter.insert(
  283. Constants.OBJ_BLOB, contentLength, in));
  284. }
  285. if (indexIter == null && headIter == null)
  286. untracked.add(entry);
  287. else
  288. wtEdits.add(new PathEdit(entry) {
  289. @Override
  290. public void apply(DirCacheEntry ent) {
  291. ent.copyMetaData(entry);
  292. }
  293. });
  294. }
  295. hasChanges = true;
  296. if (wtIter == null && headIter != null)
  297. wtDeletes.add(treeWalk.getPathString());
  298. } while (treeWalk.next());
  299. if (!hasChanges)
  300. return null;
  301. String branch = Repository.shortenRefName(head.getTarget()
  302. .getName());
  303. // Commit index changes
  304. CommitBuilder builder = createBuilder();
  305. builder.setParentId(headCommit);
  306. builder.setTreeId(cache.writeTree(inserter));
  307. builder.setMessage(MessageFormat.format(indexMessage, branch,
  308. headCommit.abbreviate(7).name(),
  309. headCommit.getShortMessage()));
  310. ObjectId indexCommit = inserter.insert(builder);
  311. // Commit untracked changes
  312. ObjectId untrackedCommit = null;
  313. if (!untracked.isEmpty()) {
  314. DirCache untrackedDirCache = DirCache.newInCore();
  315. DirCacheBuilder untrackedBuilder = untrackedDirCache
  316. .builder();
  317. for (DirCacheEntry entry : untracked)
  318. untrackedBuilder.add(entry);
  319. untrackedBuilder.finish();
  320. builder.setParentIds(new ObjectId[0]);
  321. builder.setTreeId(untrackedDirCache.writeTree(inserter));
  322. builder.setMessage(MessageFormat.format(MSG_UNTRACKED,
  323. branch, headCommit.abbreviate(7).name(),
  324. headCommit.getShortMessage()));
  325. untrackedCommit = inserter.insert(builder);
  326. }
  327. // Commit working tree changes
  328. if (!wtEdits.isEmpty() || !wtDeletes.isEmpty()) {
  329. DirCacheEditor editor = cache.editor();
  330. for (PathEdit edit : wtEdits)
  331. editor.add(edit);
  332. for (String path : wtDeletes)
  333. editor.add(new DeletePath(path));
  334. editor.finish();
  335. }
  336. builder.setParentId(headCommit);
  337. builder.addParentId(indexCommit);
  338. if (untrackedCommit != null)
  339. builder.addParentId(untrackedCommit);
  340. builder.setMessage(MessageFormat.format(
  341. workingDirectoryMessage, branch,
  342. headCommit.abbreviate(7).name(),
  343. headCommit.getShortMessage()));
  344. builder.setTreeId(cache.writeTree(inserter));
  345. commitId = inserter.insert(builder);
  346. inserter.flush();
  347. updateStashRef(commitId, builder.getAuthor(),
  348. builder.getMessage());
  349. // Remove untracked files
  350. if (includeUntracked) {
  351. for (DirCacheEntry entry : untracked) {
  352. String repoRelativePath = entry.getPathString();
  353. File file = new File(repo.getWorkTree(),
  354. repoRelativePath);
  355. FileUtils.delete(file);
  356. deletedFiles.add(repoRelativePath);
  357. }
  358. }
  359. } finally {
  360. cache.unlock();
  361. }
  362. // Hard reset to HEAD
  363. new ResetCommand(repo).setMode(ResetType.HARD).call();
  364. // Return stashed commit
  365. return parseCommit(reader, commitId);
  366. } catch (IOException e) {
  367. throw new JGitInternalException(JGitText.get().stashFailed, e);
  368. } finally {
  369. if (!deletedFiles.isEmpty()) {
  370. repo.fireEvent(
  371. new WorkingTreeModifiedEvent(null, deletedFiles));
  372. }
  373. }
  374. }
  375. }