/* Copyright (c) 2012-2014 Boundless and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/edl-v10.html
*
* Contributors:
* Gabriel Roldan (Boundless) - initial implementation
*/
package org.locationtech.geogig.api.porcelain;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import javax.annotation.Nullable;
import org.locationtech.geogig.api.AbstractGeoGigOp;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.Ref;
import org.locationtech.geogig.api.RevObject.TYPE;
import org.locationtech.geogig.api.plumbing.RefParse;
import org.locationtech.geogig.api.plumbing.ResolveObjectType;
import org.locationtech.geogig.api.plumbing.RevParse;
import org.locationtech.geogig.api.plumbing.UpdateRef;
import com.google.common.base.Optional;
/**
* Creates a new head ref (branch) pointing to the specified tree-ish or the current HEAD if no
* tree-ish was specified.
* <p>
*
* @TODO: support branch descriptions
* @TODO: support setting up the branch to track a remote branch
*/
public class BranchCreateOp extends AbstractGeoGigOp<Ref> {
private String branchName;
private String commit_ish;
private boolean checkout;
private boolean orphan;
private boolean force;
/**
* @param branchName the name of the branch to create, must not already exist
*/
public BranchCreateOp setName(final String branchName) {
this.branchName = branchName;
return this;
}
/**
* @param commit_ish either a branch ref or commit id where this branch starts at. If not set
* defaults to the current {@link Ref#HEAD HEAD}
*/
public BranchCreateOp setSource(@Nullable String commit_ish) {
this.commit_ish = commit_ish;
return this;
}
/**
* @param force true if the branch should overwrite an exisiting one with the same name, in case
* it exists
*/
public BranchCreateOp setForce(boolean force) {
this.force = force;
return this;
}
/**
* @param orphan {@code true} if the new branch shares no history with the current one, defaults
* to {@code false}
*/
public BranchCreateOp setOrphan(boolean orphan) {
this.orphan = orphan;
return this;
}
/**
* @param checkout if {@code true}, in addition to creating the new branch, a {@link CheckoutOp
* checkout} operation will be performed against the newly created branch. If the check
* out failed for any reason the {@link CheckoutException} will be propagated back to the
* caller, although the branch is guaranteed to be created and could be retrieved through
* a {@link RefParse ref-parse} op.
*/
public BranchCreateOp setAutoCheckout(boolean checkout) {
this.checkout = checkout;
return this;
}
protected Ref _call() {
checkState(branchName != null, "branch name was not provided");
final String branchRefPath = Ref.append(Ref.HEADS_PREFIX, branchName);
checkArgument(force || !command(RefParse.class).setName(branchRefPath).call().isPresent(),
"A branch named '" + branchName + "' already exists.");
Optional<Ref> branchRef;
if (orphan) {
branchRef = command(UpdateRef.class).setName(branchRefPath).setNewValue(ObjectId.NULL)
.call();
} else {
final String branchOrigin = Optional.fromNullable(commit_ish).or(Ref.HEAD);
final ObjectId branchOriginCommitId = resolveOriginCommitId(branchOrigin);
branchRef = command(UpdateRef.class).setName(branchRefPath)
.setNewValue(branchOriginCommitId).call();
checkState(branchRef.isPresent());
}
if (checkout) {
command(CheckoutOp.class).setSource(branchRefPath).call();
}
return branchRef.get();
}
private ObjectId resolveOriginCommitId(String branchOrigin) {
Optional<Ref> ref = command(RefParse.class).setName(branchOrigin).call();
if (ref.isPresent()) {
ObjectId commitId = ref.get().getObjectId();
checkArgument(!commitId.isNull(), branchOrigin
+ " has no commits yet, branch cannot be created.");
return commitId;
}
Optional<ObjectId> objectId = command(RevParse.class).setRefSpec(branchOrigin).call();
checkArgument(objectId.isPresent(), branchOrigin
+ " does not resolve to a repository object");
ObjectId commitId = objectId.get();
TYPE objectType = command(ResolveObjectType.class).setObjectId(commitId).call();
checkArgument(TYPE.COMMIT.equals(objectType), branchOrigin
+ " does not resolve to a commit: " + objectType);
return commitId;
}
}