| /* |
| * Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com> |
| * Copyright (C) 2011, Matthias Sohn <matthias.sohn@sap.com> |
| * and other copyright owners as documented in the project's IP log. |
| * |
| * This program and the accompanying materials are made available |
| * under the terms of the Eclipse Distribution License v1.0 which |
| * accompanies this distribution, is reproduced below, and is |
| * available at http://www.eclipse.org/org/documents/edl-v10.php |
| * |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or |
| * without modification, are permitted provided that the following |
| * conditions are met: |
| * |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * - Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * |
| * - Neither the name of the Eclipse Foundation, Inc. nor the |
| * names of its contributors may be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
| * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
| * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package org.eclipse.jgit.api; |
| |
| import java.io.IOException; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.EnumSet; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.jgit.api.CheckoutResult.Status; |
| import org.eclipse.jgit.api.errors.CheckoutConflictException; |
| import org.eclipse.jgit.api.errors.GitAPIException; |
| import org.eclipse.jgit.api.errors.InvalidRefNameException; |
| import org.eclipse.jgit.api.errors.JGitInternalException; |
| import org.eclipse.jgit.api.errors.RefAlreadyExistsException; |
| import org.eclipse.jgit.api.errors.RefNotFoundException; |
| import org.eclipse.jgit.dircache.DirCache; |
| import org.eclipse.jgit.dircache.DirCacheCheckout; |
| import org.eclipse.jgit.dircache.DirCacheCheckout.CheckoutMetadata; |
| import org.eclipse.jgit.dircache.DirCacheEditor; |
| import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; |
| import org.eclipse.jgit.dircache.DirCacheEntry; |
| import org.eclipse.jgit.dircache.DirCacheIterator; |
| import org.eclipse.jgit.errors.AmbiguousObjectException; |
| import org.eclipse.jgit.errors.UnmergedPathException; |
| import org.eclipse.jgit.events.WorkingTreeModifiedEvent; |
| import org.eclipse.jgit.internal.JGitText; |
| import org.eclipse.jgit.lib.AnyObjectId; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.CoreConfig.EolStreamType; |
| import org.eclipse.jgit.lib.FileMode; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.ObjectReader; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.RefUpdate; |
| import org.eclipse.jgit.lib.RefUpdate.Result; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevTree; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| import org.eclipse.jgit.treewalk.TreeWalk; |
| import org.eclipse.jgit.treewalk.filter.PathFilterGroup; |
| |
| /** |
| * Checkout a branch to the working tree. |
| * <p> |
| * Examples (<code>git</code> is a {@link Git} instance): |
| * <p> |
| * Check out an existing branch: |
| * |
| * <pre> |
| * git.checkout().setName("feature").call(); |
| * </pre> |
| * <p> |
| * Check out paths from the index: |
| * |
| * <pre> |
| * git.checkout().addPath("file1.txt").addPath("file2.txt").call(); |
| * </pre> |
| * <p> |
| * Check out a path from a commit: |
| * |
| * <pre> |
| * git.checkout().setStartPoint("HEADˆ").addPath("file1.txt").call(); |
| * </pre> |
| * |
| * <p> |
| * Create a new branch and check it out: |
| * |
| * <pre> |
| * git.checkout().setCreateBranch(true).setName("newbranch").call(); |
| * </pre> |
| * <p> |
| * Create a new tracking branch for a remote branch and check it out: |
| * |
| * <pre> |
| * git.checkout().setCreateBranch(true).setName("stable") |
| * .setUpstreamMode(SetupUpstreamMode.SET_UPSTREAM) |
| * .setStartPoint("origin/stable").call(); |
| * </pre> |
| * |
| * @see <a |
| * href="http://www.kernel.org/pub/software/scm/git/docs/git-checkout.html" |
| * >Git documentation about Checkout</a> |
| */ |
| public class CheckoutCommand extends GitCommand<Ref> { |
| |
| /** |
| * Stage to check out, see {@link CheckoutCommand#setStage(Stage)}. |
| */ |
| public static enum Stage { |
| /** |
| * Base stage (#1) |
| */ |
| BASE(DirCacheEntry.STAGE_1), |
| |
| /** |
| * Ours stage (#2) |
| */ |
| OURS(DirCacheEntry.STAGE_2), |
| |
| /** |
| * Theirs stage (#3) |
| */ |
| THEIRS(DirCacheEntry.STAGE_3); |
| |
| private final int number; |
| |
| private Stage(int number) { |
| this.number = number; |
| } |
| } |
| |
| private String name; |
| |
| private boolean force = false; |
| |
| private boolean createBranch = false; |
| |
| private boolean orphan = false; |
| |
| private CreateBranchCommand.SetupUpstreamMode upstreamMode; |
| |
| private String startPoint = null; |
| |
| private RevCommit startCommit; |
| |
| private Stage checkoutStage = null; |
| |
| private CheckoutResult status; |
| |
| private List<String> paths; |
| |
| private boolean checkoutAllPaths; |
| |
| private Set<String> actuallyModifiedPaths; |
| |
| /** |
| * @param repo |
| */ |
| protected CheckoutCommand(Repository repo) { |
| super(repo); |
| this.paths = new LinkedList<>(); |
| } |
| |
| /** |
| * @throws RefAlreadyExistsException |
| * when trying to create (without force) a branch with a name |
| * that already exists |
| * @throws RefNotFoundException |
| * if the start point or branch can not be found |
| * @throws InvalidRefNameException |
| * if the provided name is <code>null</code> or otherwise |
| * invalid |
| * @throws CheckoutConflictException |
| * if the checkout results in a conflict |
| * @return the newly created branch |
| */ |
| @Override |
| public Ref call() throws GitAPIException, RefAlreadyExistsException, |
| RefNotFoundException, InvalidRefNameException, |
| CheckoutConflictException { |
| checkCallable(); |
| try { |
| processOptions(); |
| if (checkoutAllPaths || !paths.isEmpty()) { |
| checkoutPaths(); |
| status = new CheckoutResult(Status.OK, paths); |
| setCallable(false); |
| return null; |
| } |
| |
| if (createBranch) { |
| try (Git git = new Git(repo)) { |
| CreateBranchCommand command = git.branchCreate(); |
| command.setName(name); |
| if (startCommit != null) |
| command.setStartPoint(startCommit); |
| else |
| command.setStartPoint(startPoint); |
| if (upstreamMode != null) |
| command.setUpstreamMode(upstreamMode); |
| command.call(); |
| } |
| } |
| |
| Ref headRef = repo.exactRef(Constants.HEAD); |
| if (headRef == null) { |
| // TODO Git CLI supports checkout from unborn branch, we should |
| // also allow this |
| throw new UnsupportedOperationException( |
| JGitText.get().cannotCheckoutFromUnbornBranch); |
| } |
| String shortHeadRef = getShortBranchName(headRef); |
| String refLogMessage = "checkout: moving from " + shortHeadRef; //$NON-NLS-1$ |
| ObjectId branch; |
| if (orphan) { |
| if (startPoint == null && startCommit == null) { |
| Result r = repo.updateRef(Constants.HEAD).link( |
| getBranchName()); |
| if (!EnumSet.of(Result.NEW, Result.FORCED).contains(r)) |
| throw new JGitInternalException(MessageFormat.format( |
| JGitText.get().checkoutUnexpectedResult, |
| r.name())); |
| this.status = CheckoutResult.NOT_TRIED_RESULT; |
| return repo.exactRef(Constants.HEAD); |
| } |
| branch = getStartPointObjectId(); |
| } else { |
| branch = repo.resolve(name); |
| if (branch == null) |
| throw new RefNotFoundException(MessageFormat.format( |
| JGitText.get().refNotResolved, name)); |
| } |
| |
| RevCommit headCommit = null; |
| RevCommit newCommit = null; |
| try (RevWalk revWalk = new RevWalk(repo)) { |
| AnyObjectId headId = headRef.getObjectId(); |
| headCommit = headId == null ? null |
| : revWalk.parseCommit(headId); |
| newCommit = revWalk.parseCommit(branch); |
| } |
| RevTree headTree = headCommit == null ? null : headCommit.getTree(); |
| DirCacheCheckout dco; |
| DirCache dc = repo.lockDirCache(); |
| try { |
| dco = new DirCacheCheckout(repo, headTree, dc, |
| newCommit.getTree()); |
| dco.setFailOnConflict(true); |
| try { |
| dco.checkout(); |
| } catch (org.eclipse.jgit.errors.CheckoutConflictException e) { |
| status = new CheckoutResult(Status.CONFLICTS, |
| dco.getConflicts()); |
| throw new CheckoutConflictException(dco.getConflicts(), e); |
| } |
| } finally { |
| dc.unlock(); |
| } |
| Ref ref = repo.findRef(name); |
| if (ref != null && !ref.getName().startsWith(Constants.R_HEADS)) |
| ref = null; |
| String toName = Repository.shortenRefName(name); |
| RefUpdate refUpdate = repo.updateRef(Constants.HEAD, ref == null); |
| refUpdate.setForceUpdate(force); |
| refUpdate.setRefLogMessage(refLogMessage + " to " + toName, false); //$NON-NLS-1$ |
| Result updateResult; |
| if (ref != null) |
| updateResult = refUpdate.link(ref.getName()); |
| else if (orphan) { |
| updateResult = refUpdate.link(getBranchName()); |
| ref = repo.exactRef(Constants.HEAD); |
| } else { |
| refUpdate.setNewObjectId(newCommit); |
| updateResult = refUpdate.forceUpdate(); |
| } |
| |
| setCallable(false); |
| |
| boolean ok = false; |
| switch (updateResult) { |
| case NEW: |
| ok = true; |
| break; |
| case NO_CHANGE: |
| case FAST_FORWARD: |
| case FORCED: |
| ok = true; |
| break; |
| default: |
| break; |
| } |
| |
| if (!ok) |
| throw new JGitInternalException(MessageFormat.format(JGitText |
| .get().checkoutUnexpectedResult, updateResult.name())); |
| |
| |
| if (!dco.getToBeDeleted().isEmpty()) { |
| status = new CheckoutResult(Status.NONDELETED, |
| dco.getToBeDeleted(), |
| new ArrayList<>(dco.getUpdated().keySet()), |
| dco.getRemoved()); |
| } else |
| status = new CheckoutResult(new ArrayList<>(dco |
| .getUpdated().keySet()), dco.getRemoved()); |
| |
| return ref; |
| } catch (IOException ioe) { |
| throw new JGitInternalException(ioe.getMessage(), ioe); |
| } finally { |
| if (status == null) |
| status = CheckoutResult.ERROR_RESULT; |
| } |
| } |
| |
| private String getShortBranchName(Ref headRef) { |
| if (headRef.isSymbolic()) { |
| return Repository.shortenRefName(headRef.getTarget().getName()); |
| } |
| // Detached HEAD. Every non-symbolic ref in the ref database has an |
| // object id, so this cannot be null. |
| ObjectId id = headRef.getObjectId(); |
| if (id == null) { |
| throw new NullPointerException(); |
| } |
| return id.getName(); |
| } |
| |
| /** |
| * Add a single slash-separated path to the list of paths to check out. To |
| * check out all paths, use {@link #setAllPaths(boolean)}. |
| * <p> |
| * If this option is set, neither the {@link #setCreateBranch(boolean)} nor |
| * {@link #setName(String)} option is considered. In other words, these |
| * options are exclusive. |
| * |
| * @param path |
| * path to update in the working tree and index (with |
| * <code>/</code> as separator) |
| * @return {@code this} |
| */ |
| public CheckoutCommand addPath(String path) { |
| checkCallable(); |
| this.paths.add(path); |
| return this; |
| } |
| |
| /** |
| * Add multiple slash-separated paths to the list of paths to check out. To |
| * check out all paths, use {@link #setAllPaths(boolean)}. |
| * <p> |
| * If this option is set, neither the {@link #setCreateBranch(boolean)} nor |
| * {@link #setName(String)} option is considered. In other words, these |
| * options are exclusive. |
| * |
| * @param p |
| * paths to update in the working tree and index (with |
| * <code>/</code> as separator) |
| * @return {@code this} |
| * @since 4.6 |
| */ |
| public CheckoutCommand addPaths(List<String> p) { |
| checkCallable(); |
| this.paths.addAll(p); |
| return this; |
| } |
| |
| /** |
| * Set whether to checkout all paths. |
| * <p> |
| * This options should be used when you want to do a path checkout on the |
| * entire repository and so calling {@link #addPath(String)} is not possible |
| * since empty paths are not allowed. |
| * <p> |
| * If this option is set, neither the {@link #setCreateBranch(boolean)} nor |
| * {@link #setName(String)} option is considered. In other words, these |
| * options are exclusive. |
| * |
| * @param all |
| * <code>true</code> to checkout all paths, <code>false</code> |
| * otherwise |
| * @return {@code this} |
| * @since 2.0 |
| */ |
| public CheckoutCommand setAllPaths(boolean all) { |
| checkoutAllPaths = all; |
| return this; |
| } |
| |
| /** |
| * Checkout paths into index and working directory, firing a |
| * {@link WorkingTreeModifiedEvent} if the working tree was modified. |
| * |
| * @return this instance |
| * @throws IOException |
| * @throws RefNotFoundException |
| */ |
| protected CheckoutCommand checkoutPaths() throws IOException, |
| RefNotFoundException { |
| actuallyModifiedPaths = new HashSet<>(); |
| DirCache dc = repo.lockDirCache(); |
| try (RevWalk revWalk = new RevWalk(repo); |
| TreeWalk treeWalk = new TreeWalk(repo, |
| revWalk.getObjectReader())) { |
| treeWalk.setRecursive(true); |
| if (!checkoutAllPaths) |
| treeWalk.setFilter(PathFilterGroup.createFromStrings(paths)); |
| if (isCheckoutIndex()) |
| checkoutPathsFromIndex(treeWalk, dc); |
| else { |
| RevCommit commit = revWalk.parseCommit(getStartPointObjectId()); |
| checkoutPathsFromCommit(treeWalk, dc, commit); |
| } |
| } finally { |
| try { |
| dc.unlock(); |
| } finally { |
| WorkingTreeModifiedEvent event = new WorkingTreeModifiedEvent( |
| actuallyModifiedPaths, null); |
| actuallyModifiedPaths = null; |
| if (!event.isEmpty()) { |
| repo.fireEvent(event); |
| } |
| } |
| } |
| return this; |
| } |
| |
| private void checkoutPathsFromIndex(TreeWalk treeWalk, DirCache dc) |
| throws IOException { |
| DirCacheIterator dci = new DirCacheIterator(dc); |
| treeWalk.addTree(dci); |
| |
| String previousPath = null; |
| |
| final ObjectReader r = treeWalk.getObjectReader(); |
| DirCacheEditor editor = dc.editor(); |
| while (treeWalk.next()) { |
| String path = treeWalk.getPathString(); |
| // Only add one edit per path |
| if (path.equals(previousPath)) |
| continue; |
| |
| final EolStreamType eolStreamType = treeWalk.getEolStreamType(); |
| final String filterCommand = treeWalk |
| .getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE); |
| editor.add(new PathEdit(path) { |
| @Override |
| public void apply(DirCacheEntry ent) { |
| int stage = ent.getStage(); |
| if (stage > DirCacheEntry.STAGE_0) { |
| if (checkoutStage != null) { |
| if (stage == checkoutStage.number) { |
| checkoutPath(ent, r, new CheckoutMetadata( |
| eolStreamType, filterCommand)); |
| actuallyModifiedPaths.add(path); |
| } |
| } else { |
| UnmergedPathException e = new UnmergedPathException( |
| ent); |
| throw new JGitInternalException(e.getMessage(), e); |
| } |
| } else { |
| checkoutPath(ent, r, new CheckoutMetadata(eolStreamType, |
| filterCommand)); |
| actuallyModifiedPaths.add(path); |
| } |
| } |
| }); |
| |
| previousPath = path; |
| } |
| editor.commit(); |
| } |
| |
| private void checkoutPathsFromCommit(TreeWalk treeWalk, DirCache dc, |
| RevCommit commit) throws IOException { |
| treeWalk.addTree(commit.getTree()); |
| final ObjectReader r = treeWalk.getObjectReader(); |
| DirCacheEditor editor = dc.editor(); |
| while (treeWalk.next()) { |
| final ObjectId blobId = treeWalk.getObjectId(0); |
| final FileMode mode = treeWalk.getFileMode(0); |
| final EolStreamType eolStreamType = treeWalk.getEolStreamType(); |
| final String filterCommand = treeWalk |
| .getFilterCommand(Constants.ATTR_FILTER_TYPE_SMUDGE); |
| final String path = treeWalk.getPathString(); |
| editor.add(new PathEdit(path) { |
| @Override |
| public void apply(DirCacheEntry ent) { |
| ent.setObjectId(blobId); |
| ent.setFileMode(mode); |
| checkoutPath(ent, r, |
| new CheckoutMetadata(eolStreamType, filterCommand)); |
| actuallyModifiedPaths.add(path); |
| } |
| }); |
| } |
| editor.commit(); |
| } |
| |
| private void checkoutPath(DirCacheEntry entry, ObjectReader reader, |
| CheckoutMetadata checkoutMetadata) { |
| try { |
| DirCacheCheckout.checkoutEntry(repo, entry, reader, true, |
| checkoutMetadata); |
| } catch (IOException e) { |
| throw new JGitInternalException(MessageFormat.format( |
| JGitText.get().checkoutConflictWithFile, |
| entry.getPathString()), e); |
| } |
| } |
| |
| private boolean isCheckoutIndex() { |
| return startCommit == null && startPoint == null; |
| } |
| |
| private ObjectId getStartPointObjectId() throws AmbiguousObjectException, |
| RefNotFoundException, IOException { |
| if (startCommit != null) |
| return startCommit.getId(); |
| |
| String startPointOrHead = (startPoint != null) ? startPoint |
| : Constants.HEAD; |
| ObjectId result = repo.resolve(startPointOrHead); |
| if (result == null) |
| throw new RefNotFoundException(MessageFormat.format( |
| JGitText.get().refNotResolved, startPointOrHead)); |
| return result; |
| } |
| |
| private void processOptions() throws InvalidRefNameException, |
| RefAlreadyExistsException, IOException { |
| if (((!checkoutAllPaths && paths.isEmpty()) || orphan) |
| && (name == null || !Repository |
| .isValidRefName(Constants.R_HEADS + name))) |
| throw new InvalidRefNameException(MessageFormat.format(JGitText |
| .get().branchNameInvalid, name == null ? "<null>" : name)); //$NON-NLS-1$ |
| |
| if (orphan) { |
| Ref refToCheck = repo.exactRef(getBranchName()); |
| if (refToCheck != null) |
| throw new RefAlreadyExistsException(MessageFormat.format( |
| JGitText.get().refAlreadyExists, name)); |
| } |
| } |
| |
| private String getBranchName() { |
| if (name.startsWith(Constants.R_REFS)) |
| return name; |
| |
| return Constants.R_HEADS + name; |
| } |
| |
| /** |
| * Specify the name of the branch or commit to check out, or the new branch |
| * name. |
| * <p> |
| * When only checking out paths and not switching branches, use |
| * {@link #setStartPoint(String)} or {@link #setStartPoint(RevCommit)} to |
| * specify from which branch or commit to check out files. |
| * <p> |
| * When {@link #setCreateBranch(boolean)} is set to <code>true</code>, use |
| * this method to set the name of the new branch to create and |
| * {@link #setStartPoint(String)} or {@link #setStartPoint(RevCommit)} to |
| * specify the start point of the branch. |
| * |
| * @param name |
| * the name of the branch or commit |
| * @return this instance |
| */ |
| public CheckoutCommand setName(String name) { |
| checkCallable(); |
| this.name = name; |
| return this; |
| } |
| |
| /** |
| * Specify whether to create a new branch. |
| * <p> |
| * If <code>true</code> is used, the name of the new branch must be set |
| * using {@link #setName(String)}. The commit at which to start the new |
| * branch can be set using {@link #setStartPoint(String)} or |
| * {@link #setStartPoint(RevCommit)}; if not specified, HEAD is used. Also |
| * see {@link #setUpstreamMode} for setting up branch tracking. |
| * |
| * @param createBranch |
| * if <code>true</code> a branch will be created as part of the |
| * checkout and set to the specified start point |
| * @return this instance |
| */ |
| public CheckoutCommand setCreateBranch(boolean createBranch) { |
| checkCallable(); |
| this.createBranch = createBranch; |
| return this; |
| } |
| |
| /** |
| * Specify whether to create a new orphan branch. |
| * <p> |
| * If <code>true</code> is used, the name of the new orphan branch must be |
| * set using {@link #setName(String)}. The commit at which to start the new |
| * orphan branch can be set using {@link #setStartPoint(String)} or |
| * {@link #setStartPoint(RevCommit)}; if not specified, HEAD is used. |
| * |
| * @param orphan |
| * if <code>true</code> a orphan branch will be created as part |
| * of the checkout to the specified start point |
| * @return this instance |
| * @since 3.3 |
| */ |
| public CheckoutCommand setOrphan(boolean orphan) { |
| checkCallable(); |
| this.orphan = orphan; |
| return this; |
| } |
| |
| /** |
| * Specify to force the ref update in case of a branch switch. |
| * |
| * @param force |
| * if <code>true</code> and the branch with the given name |
| * already exists, the start-point of an existing branch will be |
| * set to a new start-point; if false, the existing branch will |
| * not be changed |
| * @return this instance |
| */ |
| public CheckoutCommand setForce(boolean force) { |
| checkCallable(); |
| this.force = force; |
| return this; |
| } |
| |
| /** |
| * Set the name of the commit that should be checked out. |
| * <p> |
| * When checking out files and this is not specified or <code>null</code>, |
| * the index is used. |
| * <p> |
| * When creating a new branch, this will be used as the start point. If not |
| * specified or <code>null</code>, the current HEAD is used. |
| * |
| * @param startPoint |
| * commit name to check out |
| * @return this instance |
| */ |
| public CheckoutCommand setStartPoint(String startPoint) { |
| checkCallable(); |
| this.startPoint = startPoint; |
| this.startCommit = null; |
| checkOptions(); |
| return this; |
| } |
| |
| /** |
| * Set the commit that should be checked out. |
| * <p> |
| * When creating a new branch, this will be used as the start point. If not |
| * specified or <code>null</code>, the current HEAD is used. |
| * <p> |
| * When checking out files and this is not specified or <code>null</code>, |
| * the index is used. |
| * |
| * @param startCommit |
| * commit to check out |
| * @return this instance |
| */ |
| public CheckoutCommand setStartPoint(RevCommit startCommit) { |
| checkCallable(); |
| this.startCommit = startCommit; |
| this.startPoint = null; |
| checkOptions(); |
| return this; |
| } |
| |
| /** |
| * When creating a branch with {@link #setCreateBranch(boolean)}, this can |
| * be used to configure branch tracking. |
| * |
| * @param mode |
| * corresponds to the --track/--no-track options; may be |
| * <code>null</code> |
| * @return this instance |
| */ |
| public CheckoutCommand setUpstreamMode( |
| CreateBranchCommand.SetupUpstreamMode mode) { |
| checkCallable(); |
| this.upstreamMode = mode; |
| return this; |
| } |
| |
| /** |
| * When checking out the index, check out the specified stage (ours or |
| * theirs) for unmerged paths. |
| * <p> |
| * This can not be used when checking out a branch, only when checking out |
| * the index. |
| * |
| * @param stage |
| * the stage to check out |
| * @return this |
| */ |
| public CheckoutCommand setStage(Stage stage) { |
| checkCallable(); |
| this.checkoutStage = stage; |
| checkOptions(); |
| return this; |
| } |
| |
| /** |
| * @return the result, never <code>null</code> |
| */ |
| public CheckoutResult getResult() { |
| if (status == null) |
| return CheckoutResult.NOT_TRIED_RESULT; |
| return status; |
| } |
| |
| private void checkOptions() { |
| if (checkoutStage != null && !isCheckoutIndex()) |
| throw new IllegalStateException( |
| JGitText.get().cannotCheckoutOursSwitchBranch); |
| } |
| } |