All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jgit.notes.NoteMapMerger Maven / Gradle / Ivy

/*
 * Copyright (C) 2010, Sasa Zivkov 
 * 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.notes;

import java.io.IOException;

import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.Merger;
import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.TreeWalk;

/**
 * Three-way note tree merge.
 * 

* Direct implementation of NoteMap merger without using {@link TreeWalk} and * {@link AbstractTreeIterator} */ public class NoteMapMerger { private static final FanoutBucket EMPTY_FANOUT = new FanoutBucket(0); private static final LeafBucket EMPTY_LEAF = new LeafBucket(0); private final Repository db; private final NoteMerger noteMerger; private final MergeStrategy nonNotesMergeStrategy; private final ObjectReader reader; private final ObjectInserter inserter; private final MutableObjectId objectIdPrefix; /** * Constructs a NoteMapMerger with custom {@link NoteMerger} and custom * {@link MergeStrategy}. * * @param db * Git repository * @param noteMerger * note merger for merging conflicting changes on a note * @param nonNotesMergeStrategy * merge strategy for merging non-note entries */ public NoteMapMerger(Repository db, NoteMerger noteMerger, MergeStrategy nonNotesMergeStrategy) { this.db = db; this.reader = db.newObjectReader(); this.inserter = db.newObjectInserter(); this.noteMerger = noteMerger; this.nonNotesMergeStrategy = nonNotesMergeStrategy; this.objectIdPrefix = new MutableObjectId(); } /** * Constructs a NoteMapMerger with {@link DefaultNoteMerger} as the merger * for notes and the {@link MergeStrategy#RESOLVE} as the strategy for * resolving conflicts on non-notes * * @param db * Git repository */ public NoteMapMerger(Repository db) { this(db, new DefaultNoteMerger(), MergeStrategy.RESOLVE); } /** * Performs the merge. * * @param base * base version of the note tree * @param ours * ours version of the note tree * @param theirs * theirs version of the note tree * @return merge result as a new NoteMap * @throws IOException */ public NoteMap merge(NoteMap base, NoteMap ours, NoteMap theirs) throws IOException { try { InMemoryNoteBucket mergedBucket = merge(0, base.getRoot(), ours.getRoot(), theirs.getRoot()); inserter.flush(); return NoteMap.newMap(mergedBucket, reader); } finally { reader.release(); inserter.release(); } } /** * This method is called only when it is known that there is some difference * between base, ours and theirs. * * @param treeDepth * @param base * @param ours * @param theirs * @return merge result as an InMemoryBucket * @throws IOException */ private InMemoryNoteBucket merge(int treeDepth, InMemoryNoteBucket base, InMemoryNoteBucket ours, InMemoryNoteBucket theirs) throws IOException { InMemoryNoteBucket result; if (base instanceof FanoutBucket || ours instanceof FanoutBucket || theirs instanceof FanoutBucket) { result = mergeFanoutBucket(treeDepth, asFanout(base), asFanout(ours), asFanout(theirs)); } else { result = mergeLeafBucket(treeDepth, (LeafBucket) base, (LeafBucket) ours, (LeafBucket) theirs); } result.nonNotes = mergeNonNotes(nonNotes(base), nonNotes(ours), nonNotes(theirs)); return result; } private FanoutBucket asFanout(InMemoryNoteBucket bucket) { if (bucket == null) return EMPTY_FANOUT; if (bucket instanceof FanoutBucket) return (FanoutBucket) bucket; return ((LeafBucket) bucket).split(); } private static NonNoteEntry nonNotes(InMemoryNoteBucket b) { return b == null ? null : b.nonNotes; } private InMemoryNoteBucket mergeFanoutBucket(int treeDepth, FanoutBucket base, FanoutBucket ours, FanoutBucket theirs) throws IOException { FanoutBucket result = new FanoutBucket(treeDepth * 2); // walking through entries of base, ours, theirs for (int i = 0; i < 256; i++) { NoteBucket b = base.getBucket(i); NoteBucket o = ours.getBucket(i); NoteBucket t = theirs.getBucket(i); if (equals(o, t)) addIfNotNull(result, i, o); else if (equals(b, o)) addIfNotNull(result, i, t); else if (equals(b, t)) addIfNotNull(result, i, o); else { objectIdPrefix.setByte(treeDepth, i); InMemoryNoteBucket mergedBucket = merge(treeDepth + 1, FanoutBucket.loadIfLazy(b, objectIdPrefix, reader), FanoutBucket.loadIfLazy(o, objectIdPrefix, reader), FanoutBucket.loadIfLazy(t, objectIdPrefix, reader)); result.setBucket(i, mergedBucket); } } return result.contractIfTooSmall(objectIdPrefix, reader); } private static boolean equals(NoteBucket a, NoteBucket b) { if (a == null && b == null) return true; return a != null && b != null && a.getTreeId().equals(b.getTreeId()); } private void addIfNotNull(FanoutBucket b, int cell, NoteBucket child) throws IOException { if (child == null) return; if (child instanceof InMemoryNoteBucket) b.setBucket(cell, ((InMemoryNoteBucket) child).writeTree(inserter)); else b.setBucket(cell, child.getTreeId()); } private InMemoryNoteBucket mergeLeafBucket(int treeDepth, LeafBucket bb, LeafBucket ob, LeafBucket tb) throws MissingObjectException, IOException { bb = notNullOrEmpty(bb); ob = notNullOrEmpty(ob); tb = notNullOrEmpty(tb); InMemoryNoteBucket result = new LeafBucket(treeDepth * 2); int bi = 0, oi = 0, ti = 0; while (bi < bb.size() || oi < ob.size() || ti < tb.size()) { Note b = get(bb, bi), o = get(ob, oi), t = get(tb, ti); Note min = min(b, o, t); b = sameNoteOrNull(min, b); o = sameNoteOrNull(min, o); t = sameNoteOrNull(min, t); if (sameContent(o, t)) result = addIfNotNull(result, o); else if (sameContent(b, o)) result = addIfNotNull(result, t); else if (sameContent(b, t)) result = addIfNotNull(result, o); else result = addIfNotNull(result, noteMerger.merge(b, o, t, reader, inserter)); if (b != null) bi++; if (o != null) oi++; if (t != null) ti++; } return result; } private static LeafBucket notNullOrEmpty(LeafBucket b) { return b != null ? b : EMPTY_LEAF; } private static Note get(LeafBucket b, int i) { return i < b.size() ? b.get(i) : null; } private static Note min(Note b, Note o, Note t) { Note min = b; if (min == null || (o != null && o.compareTo(min) < 0)) min = o; if (min == null || (t != null && t.compareTo(min) < 0)) min = t; return min; } private static Note sameNoteOrNull(Note min, Note other) { return sameNote(min, other) ? other : null; } private static boolean sameNote(Note a, Note b) { if (a == null && b == null) return true; return a != null && b != null && AnyObjectId.equals(a, b); } private static boolean sameContent(Note a, Note b) { if (a == null && b == null) return true; return a != null && b != null && AnyObjectId.equals(a.getData(), b.getData()); } private static InMemoryNoteBucket addIfNotNull(InMemoryNoteBucket result, Note note) { if (note != null) return result.append(note); else return result; } private NonNoteEntry mergeNonNotes(NonNoteEntry baseList, NonNoteEntry oursList, NonNoteEntry theirsList) throws IOException { if (baseList == null && oursList == null && theirsList == null) return null; ObjectId baseId = write(baseList); ObjectId oursId = write(oursList); ObjectId theirsId = write(theirsList); inserter.flush(); Merger m = nonNotesMergeStrategy.newMerger(db, true); if (m instanceof ThreeWayMerger) ((ThreeWayMerger) m).setBase(baseId); if (!m.merge(oursId, theirsId)) throw new NotesMergeConflictException(baseList, oursList, theirsList); ObjectId resultTreeId = m.getResultTreeId(); AbbreviatedObjectId none = AbbreviatedObjectId.fromString(""); //$NON-NLS-1$ return NoteParser.parse(none, resultTreeId, reader).nonNotes; } private ObjectId write(NonNoteEntry list) throws IOException { LeafBucket b = new LeafBucket(0); b.nonNotes = list; return b.writeTree(inserter); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy