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.

CheckoutCommand.java 23KB

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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
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 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. /*
  2. * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
  3. * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.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 java.io.IOException;
  46. import java.text.MessageFormat;
  47. import java.util.ArrayList;
  48. import java.util.EnumSet;
  49. import java.util.HashSet;
  50. import java.util.LinkedList;
  51. import java.util.List;
  52. import java.util.Set;
  53. import org.eclipse.jgit.api.CheckoutResult.Status;
  54. import org.eclipse.jgit.api.errors.CheckoutConflictException;
  55. import org.eclipse.jgit.api.errors.GitAPIException;
  56. import org.eclipse.jgit.api.errors.InvalidRefNameException;
  57. import org.eclipse.jgit.api.errors.JGitInternalException;
  58. import org.eclipse.jgit.api.errors.RefAlreadyExistsException;
  59. import org.eclipse.jgit.api.errors.RefNotFoundException;
  60. import org.eclipse.jgit.dircache.DirCache;
  61. import org.eclipse.jgit.dircache.DirCacheCheckout;
  62. import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata;
  63. import org.eclipse.jgit.dircache.DirCacheEditor;
  64. import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
  65. import org.eclipse.jgit.dircache.DirCacheEntry;
  66. import org.eclipse.jgit.dircache.DirCacheIterator;
  67. import org.eclipse.jgit.errors.AmbiguousObjectException;
  68. import org.eclipse.jgit.errors.UnmergedPathException;
  69. import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
  70. import org.eclipse.jgit.internal.JGitText;
  71. import org.eclipse.jgit.lib.AnyObjectId;
  72. import org.eclipse.jgit.lib.Constants;
  73. import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
  74. import org.eclipse.jgit.lib.FileMode;
  75. import org.eclipse.jgit.lib.ObjectId;
  76. import org.eclipse.jgit.lib.ObjectReader;
  77. import org.eclipse.jgit.lib.Ref;
  78. import org.eclipse.jgit.lib.RefUpdate;
  79. import org.eclipse.jgit.lib.RefUpdate.Result;
  80. import org.eclipse.jgit.lib.Repository;
  81. import org.eclipse.jgit.revwalk.RevCommit;
  82. import org.eclipse.jgit.revwalk.RevTree;
  83. import org.eclipse.jgit.revwalk.RevWalk;
  84. import org.eclipse.jgit.treewalk.TreeWalk;
  85. import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
  86. /**
  87. * Checkout a branch to the working tree.
  88. * <p>
  89. * Examples (<code>git</code> is a {@link Git} instance):
  90. * <p>
  91. * Check out an existing branch:
  92. *
  93. * <pre>
  94. * git.checkout().setName(&quot;feature&quot;).call();
  95. * </pre>
  96. * <p>
  97. * Check out paths from the index:
  98. *
  99. * <pre>
  100. * git.checkout().addPath(&quot;file1.txt&quot;).addPath(&quot;file2.txt&quot;).call();
  101. * </pre>
  102. * <p>
  103. * Check out a path from a commit:
  104. *
  105. * <pre>
  106. * git.checkout().setStartPoint(&quot;HEAD&circ;&quot;).addPath(&quot;file1.txt&quot;).call();
  107. * </pre>
  108. *
  109. * <p>
  110. * Create a new branch and check it out:
  111. *
  112. * <pre>
  113. * git.checkout().setCreateBranch(true).setName(&quot;newbranch&quot;).call();
  114. * </pre>
  115. * <p>
  116. * Create a new tracking branch for a remote branch and check it out:
  117. *
  118. * <pre>
  119. * git.checkout().setCreateBranch(true).setName(&quot;stable&quot;)
  120. * .setUpstreamMode(SetupUpstreamMode.SET_UPSTREAM)
  121. * .setStartPoint(&quot;origin/stable&quot;).call();
  122. * </pre>
  123. *
  124. * @see <a
  125. * href="http://www.kernel.org/pub/software/scm/git/docs/git-checkout.html"
  126. * >Git documentation about Checkout</a>
  127. */
  128. public class CheckoutCommand extends GitCommand<Ref> {
  129. /**
  130. * Stage to check out, see {@link CheckoutCommand#setStage(Stage)}.
  131. */
  132. public static enum Stage {
  133. /**
  134. * Base stage (#1)
  135. */
  136. BASE(DirCacheEntry.STAGE_1),
  137. /**
  138. * Ours stage (#2)
  139. */
  140. OURS(DirCacheEntry.STAGE_2),
  141. /**
  142. * Theirs stage (#3)
  143. */
  144. THEIRS(DirCacheEntry.STAGE_3);
  145. private final int number;
  146. private Stage(int number) {
  147. this.number = number;
  148. }
  149. }
  150. private String name;
  151. private boolean force = false;
  152. private boolean createBranch = false;
  153. private boolean orphan = false;
  154. private CreateBranchCommand.SetupUpstreamMode upstreamMode;
  155. private String startPoint = null;
  156. private RevCommit startCommit;
  157. private Stage checkoutStage = null;
  158. private CheckoutResult status;
  159. private List<String> paths;
  160. private boolean checkoutAllPaths;
  161. private Set<String> actuallyModifiedPaths;
  162. /**
  163. * @param repo
  164. */
  165. protected CheckoutCommand(Repository repo) {
  166. super(repo);
  167. this.paths = new LinkedList<>();
  168. }
  169. /**
  170. * @throws RefAlreadyExistsException
  171. * when trying to create (without force) a branch with a name
  172. * that already exists
  173. * @throws RefNotFoundException
  174. * if the start point or branch can not be found
  175. * @throws InvalidRefNameException
  176. * if the provided name is <code>null</code> or otherwise
  177. * invalid
  178. * @throws CheckoutConflictException
  179. * if the checkout results in a conflict
  180. * @return the newly created branch
  181. */
  182. @Override
  183. public Ref call() throws GitAPIException, RefAlreadyExistsException,
  184. RefNotFoundException, InvalidRefNameException,
  185. CheckoutConflictException {
  186. checkCallable();
  187. try {
  188. processOptions();
  189. if (checkoutAllPaths || !paths.isEmpty()) {
  190. checkoutPaths();
  191. status = new CheckoutResult(Status.OK, paths);
  192. setCallable(false);
  193. return null;
  194. }
  195. if (createBranch) {
  196. try (Git git = new Git(repo)) {
  197. CreateBranchCommand command = git.branchCreate();
  198. command.setName(name);
  199. if (startCommit != null)
  200. command.setStartPoint(startCommit);
  201. else
  202. command.setStartPoint(startPoint);
  203. if (upstreamMode != null)
  204. command.setUpstreamMode(upstreamMode);
  205. command.call();
  206. }
  207. }
  208. Ref headRef = repo.exactRef(Constants.HEAD);
  209. if (headRef == null) {
  210. // TODO Git CLI supports checkout from unborn branch, we should
  211. // also allow this
  212. throw new UnsupportedOperationException(
  213. JGitText.get().cannotCheckoutFromUnbornBranch);
  214. }
  215. String shortHeadRef = getShortBranchName(headRef);
  216. String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$
  217. ObjectId branch;
  218. if (orphan) {
  219. if (startPoint == null && startCommit == null) {
  220. Result r = repo.updateRef(Constants.HEAD).link(
  221. getBranchName());
  222. if (!EnumSet.of(Result.NEW, Result.FORCED).contains(r))
  223. throw new JGitInternalException(MessageFormat.format(
  224. JGitText.get().checkoutUnexpectedResult,
  225. r.name()));
  226. this.status = CheckoutResult.NOT_TRIED_RESULT;
  227. return repo.exactRef(Constants.HEAD);
  228. }
  229. branch = getStartPointObjectId();
  230. } else {
  231. branch = repo.resolve(name);
  232. if (branch == null)
  233. throw new RefNotFoundException(MessageFormat.format(
  234. JGitText.get().refNotResolved, name));
  235. }
  236. RevCommit headCommit = null;
  237. RevCommit newCommit = null;
  238. try (RevWalk revWalk = new RevWalk(repo)) {
  239. AnyObjectId headId = headRef.getObjectId();
  240. headCommit = headId == null ? null
  241. : revWalk.parseCommit(headId);
  242. newCommit = revWalk.parseCommit(branch);
  243. }
  244. RevTree headTree = headCommit == null ? null : headCommit.getTree();
  245. DirCacheCheckout dco;
  246. DirCache dc = repo.lockDirCache();
  247. try {
  248. dco = new DirCacheCheckout(repo, headTree, dc,
  249. newCommit.getTree());
  250. dco.setFailOnConflict(true);
  251. try {
  252. dco.checkout();
  253. } catch (org.eclipse.jgit.errors.CheckoutConflictException e) {
  254. status = new CheckoutResult(Status.CONFLICTS,
  255. dco.getConflicts());
  256. throw new CheckoutConflictException(dco.getConflicts(), e);
  257. }
  258. } finally {
  259. dc.unlock();
  260. }
  261. Ref ref = repo.findRef(name);
  262. if (ref != null && !ref.getName().startsWith(Constants.R_HEADS))
  263. ref = null;
  264. String toName = Repository.shortenRefName(name);
  265. RefUpdate refUpdate = repo.updateRef(Constants.HEAD, ref == null);
  266. refUpdate.setForceUpdate(force);
  267. refUpdate.setRefLogMessage(refLogMessage + " to " + toName, false); //$NON-NLS-1$
  268. Result updateResult;
  269. if (ref != null)
  270. updateResult = refUpdate.link(ref.getName());
  271. else if (orphan) {
  272. updateResult = refUpdate.link(getBranchName());
  273. ref = repo.exactRef(Constants.HEAD);
  274. } else {
  275. refUpdate.setNewObjectId(newCommit);
  276. updateResult = refUpdate.forceUpdate();
  277. }
  278. setCallable(false);
  279. boolean ok = false;
  280. switch (updateResult) {
  281. case NEW:
  282. ok = true;
  283. break;
  284. case NO_CHANGE:
  285. case FAST_FORWARD:
  286. case FORCED:
  287. ok = true;
  288. break;
  289. default:
  290. break;
  291. }
  292. if (!ok)
  293. throw new JGitInternalException(MessageFormat.format(JGitText
  294. .get().checkoutUnexpectedResult, updateResult.name()));
  295. if (!dco.getToBeDeleted().isEmpty()) {
  296. status = new CheckoutResult(Status.NONDELETED,
  297. dco.getToBeDeleted(),
  298. new ArrayList<>(dco.getUpdated().keySet()),
  299. dco.getRemoved());
  300. } else
  301. status = new CheckoutResult(new ArrayList<>(dco
  302. .getUpdated().keySet()), dco.getRemoved());
  303. return ref;
  304. } catch (IOException ioe) {
  305. throw new JGitInternalException(ioe.getMessage(), ioe);
  306. } finally {
  307. if (status == null)
  308. status = CheckoutResult.ERROR_RESULT;
  309. }
  310. }
  311. private String getShortBranchName(Ref headRef) {
  312. if (headRef.isSymbolic()) {
  313. return Repository.shortenRefName(headRef.getTarget().getName());
  314. }
  315. // Detached HEAD. Every non-symbolic ref in the ref database has an
  316. // object id, so this cannot be null.
  317. ObjectId id = headRef.getObjectId();
  318. if (id == null) {
  319. throw new NullPointerException();
  320. }
  321. return id.getName();
  322. }
  323. /**
  324. * Add a single slash-separated path to the list of paths to check out. To
  325. * check out all paths, use {@link #setAllPaths(boolean)}.
  326. * <p>
  327. * If this option is set, neither the {@link #setCreateBranch(boolean)} nor
  328. * {@link #setName(String)} option is considered. In other words, these
  329. * options are exclusive.
  330. *
  331. * @param path
  332. * path to update in the working tree and index (with
  333. * <code>/</code> as separator)
  334. * @return {@code this}
  335. */
  336. public CheckoutCommand addPath(String path) {
  337. checkCallable();
  338. this.paths.add(path);
  339. return this;
  340. }
  341. /**
  342. * Add multiple slash-separated paths to the list of paths to check out. To
  343. * check out all paths, use {@link #setAllPaths(boolean)}.
  344. * <p>
  345. * If this option is set, neither the {@link #setCreateBranch(boolean)} nor
  346. * {@link #setName(String)} option is considered. In other words, these
  347. * options are exclusive.
  348. *
  349. * @param p
  350. * paths to update in the working tree and index (with
  351. * <code>/</code> as separator)
  352. * @return {@code this}
  353. * @since 4.6
  354. */
  355. public CheckoutCommand addPaths(List<String> p) {
  356. checkCallable();
  357. this.paths.addAll(p);
  358. return this;
  359. }
  360. /**
  361. * Set whether to checkout all paths.
  362. * <p>
  363. * This options should be used when you want to do a path checkout on the
  364. * entire repository and so calling {@link #addPath(String)} is not possible
  365. * since empty paths are not allowed.
  366. * <p>
  367. * If this option is set, neither the {@link #setCreateBranch(boolean)} nor
  368. * {@link #setName(String)} option is considered. In other words, these
  369. * options are exclusive.
  370. *
  371. * @param all
  372. * <code>true</code> to checkout all paths, <code>false</code>
  373. * otherwise
  374. * @return {@code this}
  375. * @since 2.0
  376. */
  377. public CheckoutCommand setAllPaths(boolean all) {
  378. checkoutAllPaths = all;
  379. return this;
  380. }
  381. /**
  382. * Checkout paths into index and working directory, firing a
  383. * {@link WorkingTreeModifiedEvent} if the working tree was modified.
  384. *
  385. * @return this instance
  386. * @throws IOException
  387. * @throws RefNotFoundException
  388. */
  389. protected CheckoutCommand checkoutPaths() throws IOException,
  390. RefNotFoundException {
  391. actuallyModifiedPaths = new HashSet<>();
  392. DirCache dc = repo.lockDirCache();
  393. try (RevWalk revWalk = new RevWalk(repo);
  394. TreeWalk treeWalk = new TreeWalk(repo,
  395. revWalk.getObjectReader())) {
  396. treeWalk.setRecursive(true);
  397. if (!checkoutAllPaths)
  398. treeWalk.setFilter(PathFilterGroup.createFromStrings(paths));
  399. if (isCheckoutIndex())
  400. checkoutPathsFromIndex(treeWalk, dc);
  401. else {
  402. RevCommit commit = revWalk.parseCommit(getStartPointObjectId());
  403. checkoutPathsFromCommit(treeWalk, dc, commit);
  404. }
  405. } finally {
  406. try {
  407. dc.unlock();
  408. } finally {
  409. WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent(
  410. actuallyModifiedPaths, null);
  411. actuallyModifiedPaths = null;
  412. if (!event.isEmpty()) {
  413. repo.fireEvent(event);
  414. }
  415. }
  416. }
  417. return this;
  418. }
  419. private void checkoutPathsFromIndex(TreeWalk treeWalk, DirCache dc)
  420. throws IOException {
  421. DirCacheIterator dci = new DirCacheIterator(dc);
  422. treeWalk.addTree(dci);
  423. String previousPath = null;
  424. final ObjectReader r = treeWalk.getObjectReader();
  425. DirCacheEditor editor = dc.editor();
  426. while (treeWalk.next()) {
  427. String path = treeWalk.getPathString();
  428. // Only add one edit per path
  429. if (path.equals(previousPath))
  430. continue;
  431. final EolStreamType eolStreamType = treeWalk.getEolStreamType();
  432. final String filterCommand = treeWalk
  433. .getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE);
  434. editor.add(new PathEdit(path) {
  435. @Override
  436. public void apply(DirCacheEntry ent) {
  437. int stage = ent.getStage();
  438. if (stage > DirCacheEntry.STAGE_0) {
  439. if (checkoutStage != null) {
  440. if (stage == checkoutStage.number) {
  441. checkoutPath(ent, r, new CheckoutMetadata(
  442. eolStreamType, filterCommand));
  443. actuallyModifiedPaths.add(path);
  444. }
  445. } else {
  446. UnmergedPathException e = new UnmergedPathException(
  447. ent);
  448. throw new JGitInternalException(e.getMessage(), e);
  449. }
  450. } else {
  451. checkoutPath(ent, r, new CheckoutMetadata(eolStreamType,
  452. filterCommand));
  453. actuallyModifiedPaths.add(path);
  454. }
  455. }
  456. });
  457. previousPath = path;
  458. }
  459. editor.commit();
  460. }
  461. private void checkoutPathsFromCommit(TreeWalk treeWalk, DirCache dc,
  462. RevCommit commit) throws IOException {
  463. treeWalk.addTree(commit.getTree());
  464. final ObjectReader r = treeWalk.getObjectReader();
  465. DirCacheEditor editor = dc.editor();
  466. while (treeWalk.next()) {
  467. final ObjectId blobId = treeWalk.getObjectId(0);
  468. final FileMode mode = treeWalk.getFileMode(0);
  469. final EolStreamType eolStreamType = treeWalk.getEolStreamType();
  470. final String filterCommand = treeWalk
  471. .getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE);
  472. final String path = treeWalk.getPathString();
  473. editor.add(new PathEdit(path) {
  474. @Override
  475. public void apply(DirCacheEntry ent) {
  476. ent.setObjectId(blobId);
  477. ent.setFileMode(mode);
  478. checkoutPath(ent, r,
  479. new CheckoutMetadata(eolStreamType, filterCommand));
  480. actuallyModifiedPaths.add(path);
  481. }
  482. });
  483. }
  484. editor.commit();
  485. }
  486. private void checkoutPath(DirCacheEntry entry, ObjectReader reader,
  487. CheckoutMetadata checkoutMetadata) {
  488. try {
  489. DirCacheCheckout.checkoutEntry(repo, entry, reader, true,
  490. checkoutMetadata);
  491. } catch (IOException e) {
  492. throw new JGitInternalException(MessageFormat.format(
  493. JGitText.get().checkoutConflictWithFile,
  494. entry.getPathString()), e);
  495. }
  496. }
  497. private boolean isCheckoutIndex() {
  498. return startCommit == null && startPoint == null;
  499. }
  500. private ObjectId getStartPointObjectId() throws AmbiguousObjectException,
  501. RefNotFoundException, IOException {
  502. if (startCommit != null)
  503. return startCommit.getId();
  504. String startPointOrHead = (startPoint != null) ? startPoint
  505. : Constants.HEAD;
  506. ObjectId result = repo.resolve(startPointOrHead);
  507. if (result == null)
  508. throw new RefNotFoundException(MessageFormat.format(
  509. JGitText.get().refNotResolved, startPointOrHead));
  510. return result;
  511. }
  512. private void processOptions() throws InvalidRefNameException,
  513. RefAlreadyExistsException, IOException {
  514. if (((!checkoutAllPaths && paths.isEmpty()) || orphan)
  515. && (name == null || !Repository
  516. .isValidRefName(Constants.R_HEADS + name)))
  517. throw new InvalidRefNameException(MessageFormat.format(JGitText
  518. .get().branchNameInvalid, name == null ? "<null>" : name)); //$NON-NLS-1$
  519. if (orphan) {
  520. Ref refToCheck = repo.exactRef(getBranchName());
  521. if (refToCheck != null)
  522. throw new RefAlreadyExistsException(MessageFormat.format(
  523. JGitText.get().refAlreadyExists, name));
  524. }
  525. }
  526. private String getBranchName() {
  527. if (name.startsWith(Constants.R_REFS))
  528. return name;
  529. return Constants.R_HEADS + name;
  530. }
  531. /**
  532. * Specify the name of the branch or commit to check out, or the new branch
  533. * name.
  534. * <p>
  535. * When only checking out paths and not switching branches, use
  536. * {@link #setStartPoint(String)} or {@link #setStartPoint(RevCommit)} to
  537. * specify from which branch or commit to check out files.
  538. * <p>
  539. * When {@link #setCreateBranch(boolean)} is set to <code>true</code>, use
  540. * this method to set the name of the new branch to create and
  541. * {@link #setStartPoint(String)} or {@link #setStartPoint(RevCommit)} to
  542. * specify the start point of the branch.
  543. *
  544. * @param name
  545. * the name of the branch or commit
  546. * @return this instance
  547. */
  548. public CheckoutCommand setName(String name) {
  549. checkCallable();
  550. this.name = name;
  551. return this;
  552. }
  553. /**
  554. * Specify whether to create a new branch.
  555. * <p>
  556. * If <code>true</code> is used, the name of the new branch must be set
  557. * using {@link #setName(String)}. The commit at which to start the new
  558. * branch can be set using {@link #setStartPoint(String)} or
  559. * {@link #setStartPoint(RevCommit)}; if not specified, HEAD is used. Also
  560. * see {@link #setUpstreamMode} for setting up branch tracking.
  561. *
  562. * @param createBranch
  563. * if <code>true</code> a branch will be created as part of the
  564. * checkout and set to the specified start point
  565. * @return this instance
  566. */
  567. public CheckoutCommand setCreateBranch(boolean createBranch) {
  568. checkCallable();
  569. this.createBranch = createBranch;
  570. return this;
  571. }
  572. /**
  573. * Specify whether to create a new orphan branch.
  574. * <p>
  575. * If <code>true</code> is used, the name of the new orphan branch must be
  576. * set using {@link #setName(String)}. The commit at which to start the new
  577. * orphan branch can be set using {@link #setStartPoint(String)} or
  578. * {@link #setStartPoint(RevCommit)}; if not specified, HEAD is used.
  579. *
  580. * @param orphan
  581. * if <code>true</code> a orphan branch will be created as part
  582. * of the checkout to the specified start point
  583. * @return this instance
  584. * @since 3.3
  585. */
  586. public CheckoutCommand setOrphan(boolean orphan) {
  587. checkCallable();
  588. this.orphan = orphan;
  589. return this;
  590. }
  591. /**
  592. * Specify to force the ref update in case of a branch switch.
  593. *
  594. * @param force
  595. * if <code>true</code> and the branch with the given name
  596. * already exists, the start-point of an existing branch will be
  597. * set to a new start-point; if false, the existing branch will
  598. * not be changed
  599. * @return this instance
  600. */
  601. public CheckoutCommand setForce(boolean force) {
  602. checkCallable();
  603. this.force = force;
  604. return this;
  605. }
  606. /**
  607. * Set the name of the commit that should be checked out.
  608. * <p>
  609. * When checking out files and this is not specified or <code>null</code>,
  610. * the index is used.
  611. * <p>
  612. * When creating a new branch, this will be used as the start point. If not
  613. * specified or <code>null</code>, the current HEAD is used.
  614. *
  615. * @param startPoint
  616. * commit name to check out
  617. * @return this instance
  618. */
  619. public CheckoutCommand setStartPoint(String startPoint) {
  620. checkCallable();
  621. this.startPoint = startPoint;
  622. this.startCommit = null;
  623. checkOptions();
  624. return this;
  625. }
  626. /**
  627. * Set the commit that should be checked out.
  628. * <p>
  629. * When creating a new branch, this will be used as the start point. If not
  630. * specified or <code>null</code>, the current HEAD is used.
  631. * <p>
  632. * When checking out files and this is not specified or <code>null</code>,
  633. * the index is used.
  634. *
  635. * @param startCommit
  636. * commit to check out
  637. * @return this instance
  638. */
  639. public CheckoutCommand setStartPoint(RevCommit startCommit) {
  640. checkCallable();
  641. this.startCommit = startCommit;
  642. this.startPoint = null;
  643. checkOptions();
  644. return this;
  645. }
  646. /**
  647. * When creating a branch with {@link #setCreateBranch(boolean)}, this can
  648. * be used to configure branch tracking.
  649. *
  650. * @param mode
  651. * corresponds to the --track/--no-track options; may be
  652. * <code>null</code>
  653. * @return this instance
  654. */
  655. public CheckoutCommand setUpstreamMode(
  656. CreateBranchCommand.SetupUpstreamMode mode) {
  657. checkCallable();
  658. this.upstreamMode = mode;
  659. return this;
  660. }
  661. /**
  662. * When checking out the index, check out the specified stage (ours or
  663. * theirs) for unmerged paths.
  664. * <p>
  665. * This can not be used when checking out a branch, only when checking out
  666. * the index.
  667. *
  668. * @param stage
  669. * the stage to check out
  670. * @return this
  671. */
  672. public CheckoutCommand setStage(Stage stage) {
  673. checkCallable();
  674. this.checkoutStage = stage;
  675. checkOptions();
  676. return this;
  677. }
  678. /**
  679. * @return the result, never <code>null</code>
  680. */
  681. public CheckoutResult getResult() {
  682. if (status == null)
  683. return CheckoutResult.NOT_TRIED_RESULT;
  684. return status;
  685. }
  686. private void checkOptions() {
  687. if (checkoutStage != null && !isCheckoutIndex())
  688. throw new IllegalStateException(
  689. JGitText.get().cannotCheckoutOursSwitchBranch);
  690. }
  691. }