use of org.locationtech.geogig.api.plumbing.merge.Conflict in project GeoGig by boundlessgeo.
the class Conflicts method runInternal.
@Override
public void runInternal(GeogigCLI cli) throws IOException {
checkParameter(!(idsOnly && previewDiff), "Cannot use --diff and --ids-only at the same time");
checkParameter(!(refspecsOnly && previewDiff), "Cannot use --diff and --refspecs-only at the same time");
checkParameter(!(refspecsOnly && idsOnly), "Cannot use --ids-only and --refspecs-only at the same time");
geogig = cli.getGeogig();
List<Conflict> conflicts = geogig.command(ConflictsReadOp.class).call();
if (conflicts.isEmpty()) {
cli.getConsole().println("No elements need merging.");
return;
}
for (Conflict conflict : conflicts) {
if (paths.isEmpty() || paths.contains(conflict.getPath())) {
if (previewDiff) {
printConflictDiff(conflict, cli.getConsole(), geogig);
} else if (idsOnly) {
cli.getConsole().println(conflict.toString());
} else if (refspecsOnly) {
printRefspecs(conflict, cli.getConsole(), geogig);
} else {
printConflict(conflict, cli.getConsole(), geogig);
}
}
}
}
use of org.locationtech.geogig.api.plumbing.merge.Conflict in project GeoGig by boundlessgeo.
the class AddOpTest method testInsertionAndAdditionFixesConflict.
@Test
public void testInsertionAndAdditionFixesConflict() throws Exception {
Feature points1Modified = feature(pointsType, idP1, "StringProp1_2", new Integer(1000), "POINT(1 1)");
Feature points1ModifiedB = feature(pointsType, idP1, "StringProp1_3", new Integer(2000), "POINT(1 1)");
insertAndAdd(points1);
geogig.command(CommitOp.class).call();
geogig.command(BranchCreateOp.class).setName("TestBranch").call();
insertAndAdd(points1Modified);
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("TestBranch").call();
insertAndAdd(points1ModifiedB);
insertAndAdd(points2);
geogig.command(CommitOp.class).call();
geogig.command(CheckoutOp.class).setSource("master").call();
Ref branch = geogig.command(RefParse.class).setName("TestBranch").call().get();
try {
geogig.command(MergeOp.class).addCommit(Suppliers.ofInstance(branch.getObjectId())).call();
fail();
} catch (MergeConflictsException e) {
assertTrue(e.getMessage().contains("conflict"));
}
insert(points1);
geogig.command(AddOp.class).call();
List<Conflict> conflicts = geogig.getRepository().stagingDatabase().getConflicts(null, null);
assertTrue(conflicts.isEmpty());
geogig.command(CommitOp.class).call();
Optional<Ref> ref = geogig.command(RefParse.class).setName(Ref.MERGE_HEAD).call();
assertFalse(ref.isPresent());
}
use of org.locationtech.geogig.api.plumbing.merge.Conflict in project GeoGig by boundlessgeo.
the class MergeOp method _call.
/**
* Executes the merge operation.
*
* @return always {@code true}
*/
@Override
protected MergeReport _call() throws RuntimeException {
Preconditions.checkArgument(commits.size() > 0, "No commits specified for merge.");
Preconditions.checkArgument(!(ours && theirs), "Cannot use both --ours and --theirs.");
final Optional<Ref> currHead = command(RefParse.class).setName(Ref.HEAD).call();
Preconditions.checkState(currHead.isPresent(), "Repository has no HEAD, can't rebase.");
Ref headRef = currHead.get();
ObjectId oursId = headRef.getObjectId();
// Preconditions.checkState(currHead.get() instanceof SymRef,
// "Can't rebase from detached HEAD");
// SymRef headRef = (SymRef) currHead.get();
// final String currentBranch = headRef.getTarget();
getProgressListener().started();
boolean fastForward = true;
boolean changed = false;
Optional<MergeScenarioReport> mergeScenario = Optional.absent();
List<CommitAncestorPair> pairs = Lists.newArrayList();
boolean hasConflictsOrAutomerge;
List<RevCommit> revCommits = Lists.newArrayList();
if (!ObjectId.NULL.equals(headRef.getObjectId())) {
revCommits.add(repository().getCommit(headRef.getObjectId()));
}
for (ObjectId commitId : commits) {
revCommits.add(repository().getCommit(commitId));
}
hasConflictsOrAutomerge = command(CheckMergeScenarioOp.class).setCommits(revCommits).call().booleanValue();
if (hasConflictsOrAutomerge && !theirs) {
Preconditions.checkState(commits.size() < 2, "Conflicted merge.\nCannot merge more than two commits when conflicts exist" + " or features have been modified in several histories");
RevCommit headCommit = repository().getCommit(headRef.getObjectId());
ObjectId commitId = commits.get(0);
Preconditions.checkArgument(!ObjectId.NULL.equals(commitId), "Cannot merge a NULL commit.");
Preconditions.checkArgument(repository().commitExists(commitId), "Not a valid commit: " + commitId.toString());
final RevCommit targetCommit = repository().getCommit(commitId);
Optional<ObjectId> ancestorCommit = command(FindCommonAncestor.class).setLeft(headCommit).setRight(targetCommit).call();
pairs.add(new CommitAncestorPair(commitId, ancestorCommit.get()));
mergeScenario = Optional.of(command(ReportMergeScenarioOp.class).setMergeIntoCommit(headCommit).setToMergeCommit(targetCommit).call());
List<FeatureInfo> merged = mergeScenario.get().getMerged();
for (FeatureInfo feature : merged) {
this.workingTree().insert(NodeRef.parentPath(feature.getPath()), feature.getFeature());
Iterator<DiffEntry> unstaged = workingTree().getUnstaged(null);
index().stage(getProgressListener(), unstaged, 0);
changed = true;
fastForward = false;
}
List<DiffEntry> unconflicting = mergeScenario.get().getUnconflicted();
if (!unconflicting.isEmpty()) {
index().stage(getProgressListener(), unconflicting.iterator(), 0);
changed = true;
fastForward = false;
}
workingTree().updateWorkHead(index().getTree().getId());
List<Conflict> conflicts = mergeScenario.get().getConflicts();
if (!ours && !conflicts.isEmpty()) {
// In case we use the "ours" strategy, we do nothing. We ignore conflicting
// changes and leave the current elements
command(UpdateRef.class).setName(Ref.MERGE_HEAD).setNewValue(commitId).call();
command(UpdateRef.class).setName(Ref.ORIG_HEAD).setNewValue(headCommit.getId()).call();
command(ConflictsWriteOp.class).setConflicts(conflicts).call();
StringBuilder msg = new StringBuilder();
Optional<Ref> ref = command(ResolveBranchId.class).setObjectId(commitId).call();
if (ref.isPresent()) {
msg.append("Merge branch " + ref.get().getName());
} else {
msg.append("Merge commit '" + commitId.toString() + "'. ");
}
msg.append("\n\nConflicts:\n");
for (Conflict conflict : mergeScenario.get().getConflicts()) {
msg.append("\t" + conflict.getPath() + "\n");
}
command(SaveMergeCommitMessageOp.class).setMessage(msg.toString()).call();
StringBuilder sb = new StringBuilder();
for (Conflict conflict : conflicts) {
sb.append("CONFLICT: Merge conflict in " + conflict.getPath() + "\n");
}
sb.append("Automatic merge failed. Fix conflicts and then commit the result.\n");
throw new MergeConflictsException(sb.toString(), headCommit.getId(), commitId);
}
} else {
Preconditions.checkState(!hasConflictsOrAutomerge || commits.size() < 2, "Conflicted merge.\nCannot merge more than two commits when conflicts exist" + " or features have been modified in several histories");
for (ObjectId commitId : commits) {
ProgressListener subProgress = subProgress(100.f / commits.size());
Preconditions.checkArgument(!ObjectId.NULL.equals(commitId), "Cannot merge a NULL commit.");
Preconditions.checkArgument(repository().commitExists(commitId), "Not a valid commit: " + commitId.toString());
subProgress.started();
if (ObjectId.NULL.equals(headRef.getObjectId())) {
// Fast-forward
if (headRef instanceof SymRef) {
final String currentBranch = ((SymRef) headRef).getTarget();
command(UpdateRef.class).setName(currentBranch).setNewValue(commitId).call();
headRef = (SymRef) command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call().get();
} else {
headRef = command(UpdateRef.class).setName(headRef.getName()).setNewValue(commitId).call().get();
}
workingTree().updateWorkHead(commitId);
index().updateStageHead(commitId);
subProgress.complete();
changed = true;
continue;
}
RevCommit headCommit = repository().getCommit(headRef.getObjectId());
final RevCommit targetCommit = repository().getCommit(commitId);
Optional<ObjectId> ancestorCommit = command(FindCommonAncestor.class).setLeft(headCommit).setRight(targetCommit).call();
pairs.add(new CommitAncestorPair(commitId, ancestorCommit.get()));
subProgress.setProgress(10.f);
Preconditions.checkState(ancestorCommit.isPresent(), "No ancestor commit could be found.");
if (commits.size() == 1) {
mergeScenario = Optional.of(command(ReportMergeScenarioOp.class).setMergeIntoCommit(headCommit).setToMergeCommit(targetCommit).call());
if (ancestorCommit.get().equals(headCommit.getId())) {
// Fast-forward
if (headRef instanceof SymRef) {
final String currentBranch = ((SymRef) headRef).getTarget();
command(UpdateRef.class).setName(currentBranch).setNewValue(commitId).call();
headRef = (SymRef) command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call().get();
} else {
headRef = command(UpdateRef.class).setName(headRef.getName()).setNewValue(commitId).call().get();
}
workingTree().updateWorkHead(commitId);
index().updateStageHead(commitId);
subProgress.complete();
changed = true;
continue;
} else if (ancestorCommit.get().equals(commitId)) {
continue;
}
}
// get changes
Iterator<DiffEntry> diff = command(DiffTree.class).setOldTree(ancestorCommit.get()).setNewTree(targetCommit.getId()).setReportTrees(true).call();
// stage changes
index().stage(new SubProgressListener(subProgress, 100.f), diff, 0);
changed = true;
fastForward = false;
workingTree().updateWorkHead(index().getTree().getId());
subProgress.complete();
}
}
if (!changed) {
throw new NothingToCommitException("The branch has already been merged.");
}
RevCommit mergeCommit = commit(fastForward);
MergeReport result = new MergeReport(mergeCommit, mergeScenario, oursId, pairs);
return result;
}
use of org.locationtech.geogig.api.plumbing.merge.Conflict in project GeoGig by boundlessgeo.
the class RebaseOp method _call.
/**
* Executes the rebase operation.
*
* @return always {@code true}
*/
@Override
protected Boolean _call() {
final Optional<Ref> currHead = command(RefParse.class).setName(Ref.HEAD).call();
Preconditions.checkState(currHead.isPresent(), "Repository has no HEAD, can't rebase.");
if (!(continueRebase || skip || abort)) {
Preconditions.checkState(currHead.get() instanceof SymRef, "Can't rebase from detached HEAD %s", currHead.get());
Preconditions.checkState(upstream != null, "No upstream target has been specified.");
Preconditions.checkState(!ObjectId.NULL.equals(upstream.get()), "Upstream did not resolve to a commit.");
}
// Rebase can only be run in a conflicted situation if the skip or abort option is used
List<Conflict> conflicts = command(ConflictsReadOp.class).call();
Preconditions.checkState(conflicts.isEmpty() || skip || abort, "Cannot run operation while merge conflicts exist.");
Optional<Ref> ref = command(RefParse.class).setName(Ref.ORIG_HEAD).call();
File branchFile = new File(getRebaseFolder(), "branch");
RevCommit squashCommit = readSquashCommit();
if (abort) {
Preconditions.checkState(ref.isPresent() && branchFile.exists(), "Cannot abort. You are not in the middle of a rebase process.");
command(ResetOp.class).setMode(ResetMode.HARD).setCommit(Suppliers.ofInstance(ref.get().getObjectId())).call();
command(UpdateRef.class).setDelete(true).setName(Ref.ORIG_HEAD).call();
branchFile.delete();
return true;
} else if (continueRebase) {
Preconditions.checkState(ref.isPresent() && branchFile.exists(), "Cannot continue. You are not in the middle of a rebase process.");
try {
currentBranch = Files.readFirstLine(branchFile, Charsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Cannot read current branch info file");
}
rebaseHead = currHead.get().getObjectId();
if (squashCommit == null) {
// Commit the manually-merged changes with the info of the commit that caused the
// conflict
applyNextCommit(false);
// Commit files should already be prepared, so we do nothing else
} else {
applyCommit(squashCommit, false);
}
} else if (skip) {
Preconditions.checkState(ref.isPresent() && branchFile.exists(), "Cannot skip. You are not in the middle of a rebase process.");
try {
currentBranch = Files.readFirstLine(branchFile, Charsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Cannot read current branch info file");
}
rebaseHead = currHead.get().getObjectId();
command(ResetOp.class).setCommit(Suppliers.ofInstance(rebaseHead)).setMode(ResetMode.HARD).call();
if (squashCommit == null) {
skipCurrentCommit();
applyNextCommit(true);
} else {
return true;
}
} else {
Preconditions.checkState(!ref.isPresent(), "You are currently in the middle of a merge or rebase project <ORIG_HEAD is present>.");
getProgressListener().started();
command(UpdateRef.class).setName(Ref.ORIG_HEAD).setNewValue(currHead.get().getObjectId()).call();
// Here we prepare the files with the info about the commits to apply or, if that's not
// needed, do a fast-forward
final SymRef headRef = (SymRef) currHead.get();
currentBranch = headRef.getTarget();
if (ObjectId.NULL.equals(headRef.getObjectId())) {
// Fast-forward
command(UpdateRef.class).setName(currentBranch).setNewValue(upstream.get()).call();
command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call();
workingTree().updateWorkHead(upstream.get());
index().updateStageHead(upstream.get());
getProgressListener().complete();
return true;
}
Repository repository = repository();
final RevCommit headCommit = repository.getCommit(headRef.getObjectId());
final RevCommit targetCommit = repository.getCommit(upstream.get());
command(UpdateRef.class).setName(Ref.ORIG_HEAD).setNewValue(headCommit.getId());
Optional<ObjectId> ancestorCommit = command(FindCommonAncestor.class).setLeft(headCommit).setRight(targetCommit).setProgressListener(subProgress(10.f)).call();
Preconditions.checkState(ancestorCommit.isPresent(), "No ancestor commit could be found.");
if (ancestorCommit.get().equals(headCommit.getId())) {
// Fast-forward
command(UpdateRef.class).setName(currentBranch).setNewValue(upstream.get()).call();
command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call();
workingTree().updateWorkHead(upstream.get());
index().updateStageHead(upstream.get());
getProgressListener().complete();
return true;
}
// Get all commits between the head commit and the ancestor.
Iterator<RevCommit> commitIterator = command(LogOp.class).call();
List<RevCommit> commitsToRebase = new ArrayList<RevCommit>();
RevCommit commit = commitIterator.next();
while (!commit.getId().equals(ancestorCommit.get())) {
commitsToRebase.add(commit);
commit = commitIterator.next();
}
// rewind the HEAD
if (onto == null) {
onto = Suppliers.ofInstance(upstream.get());
}
rebaseHead = onto.get();
command(ResetOp.class).setCommit(Suppliers.ofInstance(rebaseHead)).setMode(ResetMode.HARD).call();
if (squashMessage != null) {
CommitBuilder builder = new CommitBuilder(commitsToRebase.get(0));
builder.setParentIds(Arrays.asList(ancestorCommit.get()));
builder.setMessage(squashMessage);
squashCommit = builder.build();
// save the commit, since it does not exist in the database, and might be needed if
// there is a conflict
CharSequence commitString = command(CatObject.class).setObject(Suppliers.ofInstance(squashCommit)).call();
File squashFile = new File(getRebaseFolder(), "squash");
try {
Files.write(commitString, squashFile, Charsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Cannot create squash commit info file");
}
applyCommit(squashCommit, true);
return true;
} else {
createRebaseCommitsInfoFiles(commitsToRebase);
}
// ProgressListener subProgress = subProgress(90.f);
}
if (squashCommit == null) {
boolean ret;
do {
ret = applyNextCommit(true);
} while (ret);
}
// clean up
File squashFile = new File(getRebaseFolder(), "squash");
if (squashFile.exists()) {
squashFile.delete();
}
command(UpdateRef.class).setDelete(true).setName(Ref.ORIG_HEAD).call();
branchFile.delete();
// subProgress.complete();
getProgressListener().complete();
return true;
}
use of org.locationtech.geogig.api.plumbing.merge.Conflict in project GeoGig by boundlessgeo.
the class RebaseOp method applyCommit.
/**
* Applies the passed command.
*
* @param commitToApply the commit to apply
* @param useCommitChanges if true, applies the command completely, staging its changes before
* committing. If false, it commits the currently staged changes, ignoring the changes in
* the commit and using just its author and message
*/
private void applyCommit(RevCommit commitToApply, boolean useCommitChanges) {
Repository repository = repository();
Platform platform = platform();
if (useCommitChanges) {
ObjectId parentTreeId;
ObjectId parentCommitId = ObjectId.NULL;
if (commitToApply.getParentIds().size() > 0) {
parentCommitId = commitToApply.getParentIds().get(0);
}
parentTreeId = ObjectId.NULL;
if (repository.commitExists(parentCommitId)) {
parentTreeId = repository.getCommit(parentCommitId).getTreeId();
}
// get changes
Iterator<DiffEntry> diff = command(DiffTree.class).setOldTree(parentTreeId).setNewTree(commitToApply.getTreeId()).setReportTrees(true).call();
// see if there are conflicts
MergeScenarioReport report = command(ReportCommitConflictsOp.class).setCommit(commitToApply).call();
if (report.getConflicts().isEmpty()) {
// stage changes
index().stage(getProgressListener(), diff, 0);
// write new tree
ObjectId newTreeId = command(WriteTree2.class).call();
long timestamp = platform.currentTimeMillis();
// Create new commit
CommitBuilder builder = new CommitBuilder(commitToApply);
builder.setParentIds(Arrays.asList(rebaseHead));
builder.setTreeId(newTreeId);
builder.setCommitterTimestamp(timestamp);
builder.setCommitterTimeZoneOffset(platform.timeZoneOffset(timestamp));
RevCommit newCommit = builder.build();
repository.objectDatabase().put(newCommit);
rebaseHead = newCommit.getId();
command(UpdateRef.class).setName(currentBranch).setNewValue(rebaseHead).call();
command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call();
workingTree().updateWorkHead(newTreeId);
index().updateStageHead(newTreeId);
} else {
Iterator<DiffEntry> unconflicted = report.getUnconflicted().iterator();
// stage unconflicted changes
index().stage(getProgressListener(), unconflicted, 0);
workingTree().updateWorkHead(index().getTree().getId());
// mark conflicted elements
command(ConflictsWriteOp.class).setConflicts(report.getConflicts()).call();
// created exception message
StringBuilder msg = new StringBuilder();
msg.append("error: could not apply ");
msg.append(commitToApply.getId().toString().substring(0, 7));
msg.append(" " + commitToApply.getMessage() + "\n");
for (Conflict conflict : report.getConflicts()) {
msg.append("CONFLICT: conflict in " + conflict.getPath() + "\n");
}
File branchFile = new File(getRebaseFolder(), "branch");
try {
Files.write(currentBranch, branchFile, Charsets.UTF_8);
} catch (IOException e) {
throw new IllegalStateException("Cannot create current branch info file");
}
throw new RebaseConflictsException(msg.toString());
}
} else {
// write new tree
ObjectId newTreeId = command(WriteTree2.class).call();
long timestamp = platform.currentTimeMillis();
// Create new commit
CommitBuilder builder = new CommitBuilder(commitToApply);
builder.setParentIds(Arrays.asList(rebaseHead));
builder.setTreeId(newTreeId);
builder.setCommitterTimestamp(timestamp);
builder.setCommitterTimeZoneOffset(platform.timeZoneOffset(timestamp));
RevCommit newCommit = builder.build();
repository.objectDatabase().put(newCommit);
rebaseHead = newCommit.getId();
command(UpdateRef.class).setName(currentBranch).setNewValue(rebaseHead).call();
command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call();
workingTree().updateWorkHead(newTreeId);
index().updateStageHead(newTreeId);
}
}
Aggregations