/*
* #%L
* JavaHg
* %%
* Copyright (C) 2011 aragost Trifork ag
* %%
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
* #L%
*/
package com.aragost.javahg.commands;
import java.io.IOException;
import com.aragost.javahg.Changeset;
import com.aragost.javahg.Repository;
import com.aragost.javahg.commands.flags.GraftCommandFlags;
import com.aragost.javahg.internals.HgInputStream;
import com.aragost.javahg.internals.RuntimeIOException;
import com.aragost.javahg.merge.GraftContext;
/**
* Command class for executing <tt>hg graft</tt>. Set flags from
* {@link GraftCommandFlags} and call the {@link #execute} method.
*/
public class GraftCommand extends GraftCommandFlags {
private boolean hasConflicts = false;
private boolean somethingSkipped = false;
/**
* @param repository
* the repository associated with this command.
*/
public GraftCommand(Repository repository) {
super(repository);
cmdAppend("-y");
cmdAppend("--tool", "internal:fail");
}
@Override
public boolean isSuccessful() {
return super.isSuccessful() || this.hasConflicts || this.somethingSkipped;
}
/**
* Run <tt>hg graft</tt> with the specified revisions.
* <p>
* If the grafting is succesful null is return. Otherwise a
* MergeContext is return where the conflicts can be inspected and
* resolved.
*
* @param changeset
* the changeset to graft
*/
public GraftContext execute(Changeset changeset) {
Repository repo = getRepository();
repo.lock();
HgInputStream stream = launchStream(changeset.getNode());
try {
if (stream.match("grafting revision ".getBytes())) {
int rev = stream.revisionUpTo('\n');
GraftContext ctx = new GraftContext(this, rev);
if (!stream.isEof()) {
ctx.processStream(stream, true);
}
stream.consumeAll();
boolean flagOrKeepDeleteConflicts = !ctx.getFlagConflicts().isEmpty()
|| !ctx.getKeepDeleteConflicts().isEmpty();
if (getReturnCode() != 0 && this.hasConflicts || flagOrKeepDeleteConflicts) {
if (flagOrKeepDeleteConflicts && ctx.getMergeConflicts().isEmpty()) {
// graft has actually made a new changeset,
// but
// there is actually flag conflict or
// keep/delete
// conflict. So rollback the changeset.
Changeset rollbackChangeset = repo.tip();
if (!changeset.getNode().equals(rollbackChangeset.getExtra().getString("source"))) {
throw new IllegalStateException("Current tip is not grafted from expected changeset");
}
RollbackCommand.on(repo).execute();
ctx.setRollbackChangeset(rollbackChangeset);
}
return ctx;
} else {
return null;
}
} else {
return null;
}
} catch (IOException e) {
throw new RuntimeIOException(e);
} finally {
repo.unlock();
}
}
/**
* Execute the graft command with the continue flag.
*
* @return true if a new changeset was created, false if for some
* reason graft did not succeed
*/
public boolean executeContinue() {
launchString("--continue");
return getReturnCode() == 0;
}
@Override
protected void doneHook() {
String errorString = getErrorString();
this.hasConflicts = errorString.indexOf("abort: unresolved conflicts, can't continue") >= 0
|| errorString.indexOf("abort: unresolved merge conflicts (see hg help resolve)") >= 0;
this.somethingSkipped = errorString.indexOf("skipping ancestor revision ") >= 0;
}
@Override
protected void clear() {
super.clear();
this.hasConflicts = false;
this.somethingSkipped = false;
}
}