org.eclipse.jgit.lib;version="[0.10.0,0.11.0)",
org.eclipse.jgit.merge;version="[0.10.0,0.11.0)",
org.eclipse.jgit.nls;version="[0.10.0,0.11.0)",
+ org.eclipse.jgit.notes;version="[0.10.0,0.11.0)",
org.eclipse.jgit.patch;version="[0.10.0,0.11.0)",
org.eclipse.jgit.pgm;version="[0.10.0,0.11.0)",
org.eclipse.jgit.revplot;version="[0.10.0,0.11.0)",
--- /dev/null
+/*
+ * Copyright (C) 2010, Google Inc.
+ * 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 org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryTestCase;
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.util.RawParseUtils;
+
+public class NoteMapTest extends RepositoryTestCase {
+ private TestRepository<Repository> tr;
+
+ private ObjectReader reader;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ tr = new TestRepository<Repository>(db);
+ reader = db.newObjectReader();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ reader.release();
+ super.tearDown();
+ }
+
+ public void testReadFlatTwoNotes() throws Exception {
+ RevBlob a = tr.blob("a");
+ RevBlob b = tr.blob("b");
+ RevBlob data1 = tr.blob("data1");
+ RevBlob data2 = tr.blob("data2");
+
+ RevCommit r = tr.commit() //
+ .add(a.name(), data1) //
+ .add(b.name(), data2) //
+ .create();
+ tr.parseBody(r);
+
+ NoteMap map = NoteMap.read(reader, r);
+ assertNotNull("have map", map);
+
+ assertTrue("has note for a", map.contains(a));
+ assertTrue("has note for b", map.contains(b));
+ assertEquals(data1, map.get(a));
+ assertEquals(data2, map.get(b));
+
+ assertFalse("no note for data1", map.contains(data1));
+ assertNull("no note for data1", map.get(data1));
+ }
+
+ public void testReadFanout2_38() throws Exception {
+ RevBlob a = tr.blob("a");
+ RevBlob b = tr.blob("b");
+ RevBlob data1 = tr.blob("data1");
+ RevBlob data2 = tr.blob("data2");
+
+ RevCommit r = tr.commit() //
+ .add(fanout(2, a.name()), data1) //
+ .add(fanout(2, b.name()), data2) //
+ .create();
+ tr.parseBody(r);
+
+ NoteMap map = NoteMap.read(reader, r);
+ assertNotNull("have map", map);
+
+ assertTrue("has note for a", map.contains(a));
+ assertTrue("has note for b", map.contains(b));
+ assertEquals(data1, map.get(a));
+ assertEquals(data2, map.get(b));
+
+ assertFalse("no note for data1", map.contains(data1));
+ assertNull("no note for data1", map.get(data1));
+ }
+
+ public void testReadFanout2_2_36() throws Exception {
+ RevBlob a = tr.blob("a");
+ RevBlob b = tr.blob("b");
+ RevBlob data1 = tr.blob("data1");
+ RevBlob data2 = tr.blob("data2");
+
+ RevCommit r = tr.commit() //
+ .add(fanout(4, a.name()), data1) //
+ .add(fanout(4, b.name()), data2) //
+ .create();
+ tr.parseBody(r);
+
+ NoteMap map = NoteMap.read(reader, r);
+ assertNotNull("have map", map);
+
+ assertTrue("has note for a", map.contains(a));
+ assertTrue("has note for b", map.contains(b));
+ assertEquals(data1, map.get(a));
+ assertEquals(data2, map.get(b));
+
+ assertFalse("no note for data1", map.contains(data1));
+ assertNull("no note for data1", map.get(data1));
+ }
+
+ public void testReadFullyFannedOut() throws Exception {
+ RevBlob a = tr.blob("a");
+ RevBlob b = tr.blob("b");
+ RevBlob data1 = tr.blob("data1");
+ RevBlob data2 = tr.blob("data2");
+
+ RevCommit r = tr.commit() //
+ .add(fanout(38, a.name()), data1) //
+ .add(fanout(38, b.name()), data2) //
+ .create();
+ tr.parseBody(r);
+
+ NoteMap map = NoteMap.read(reader, r);
+ assertNotNull("have map", map);
+
+ assertTrue("has note for a", map.contains(a));
+ assertTrue("has note for b", map.contains(b));
+ assertEquals(data1, map.get(a));
+ assertEquals(data2, map.get(b));
+
+ assertFalse("no note for data1", map.contains(data1));
+ assertNull("no note for data1", map.get(data1));
+ }
+
+ public void testGetCachedBytes() throws Exception {
+ final String exp = "this is test data";
+ RevBlob a = tr.blob("a");
+ RevBlob data = tr.blob(exp);
+
+ RevCommit r = tr.commit() //
+ .add(a.name(), data) //
+ .create();
+ tr.parseBody(r);
+
+ NoteMap map = NoteMap.read(reader, r);
+ byte[] act = map.getCachedBytes(a, exp.length() * 4);
+ assertNotNull("has data for a", act);
+ assertEquals(exp, RawParseUtils.decode(act));
+ }
+
+ private static String fanout(int prefix, String name) {
+ StringBuilder r = new StringBuilder();
+ int i = 0;
+ for (; i < prefix && i < name.length(); i += 2) {
+ if (i != 0)
+ r.append('/');
+ r.append(name.charAt(i + 0));
+ r.append(name.charAt(i + 1));
+ }
+ if (i < name.length()) {
+ if (i != 0)
+ r.append('/');
+ r.append(name.substring(i));
+ }
+ return r.toString();
+ }
+}
org.eclipse.jgit.lib;version="0.10.0",
org.eclipse.jgit.merge;version="0.10.0",
org.eclipse.jgit.nls;version="0.10.0",
+ org.eclipse.jgit.notes;version="0.10.0",
org.eclipse.jgit.patch;version="0.10.0",
org.eclipse.jgit.revplot;version="0.10.0",
org.eclipse.jgit.revwalk;version="0.10.0",
--- /dev/null
+/*
+ * Copyright (C) 2010, Google Inc.
+ * 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 org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+
+/** In-memory representation of a single note attached to one object. */
+class Note extends ObjectId {
+ private ObjectId data;
+
+ /**
+ * A Git note about the object referenced by {@code noteOn}.
+ *
+ * @param noteOn
+ * the object that has a note attached to it.
+ * @param noteData
+ * the actual note data contained in this note
+ */
+ Note(AnyObjectId noteOn, ObjectId noteData) {
+ super(noteOn);
+ data = noteData;
+ }
+
+ ObjectId getData() {
+ return data;
+ }
+
+ void setData(ObjectId newData) {
+ data = newData;
+ }
+
+ @Override
+ public String toString() {
+ return "Note[" + name() + " -> " + data.name() + "]";
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2010, Google Inc.
+ * 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.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdSubclassMap;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Index of notes from a note branch.
+ *
+ * This class is not thread-safe, and relies on an {@link ObjectReader} that it
+ * borrows/shares with the caller.
+ *
+ * The current implementation is not very efficient. During load it recursively
+ * scans the entire note branch and indexes all annotated objects. If there are
+ * more than 256 notes in the branch, and not all of them will be accessed by
+ * the caller, this aggressive up-front loading probably takes too much time.
+ */
+public class NoteMap {
+ /**
+ * Load a collection of notes from a branch.
+ *
+ * @param reader
+ * reader to scan the note branch with. This reader may be
+ * retained by the NoteMap for the life of the map in order to
+ * support lazy loading of entries.
+ * @param commit
+ * the revision of the note branch to read.
+ * @return the note map read from the commit.
+ * @throws IOException
+ * the repository cannot be accessed through the reader.
+ * @throws CorruptObjectException
+ * a tree object is corrupt and cannot be read.
+ * @throws IncorrectObjectTypeException
+ * a tree object wasn't actually a tree.
+ * @throws MissingObjectException
+ * a reference tree object doesn't exist.
+ */
+ public static NoteMap read(ObjectReader reader, RevCommit commit)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ CorruptObjectException, IOException {
+ return read(reader, commit.getTree());
+ }
+
+ /**
+ * Load a collection of notes from a tree.
+ *
+ * @param reader
+ * reader to scan the note branch with. This reader may be
+ * retained by the NoteMap for the life of the map in order to
+ * support lazy loading of entries.
+ * @param tree
+ * the note tree to read.
+ * @return the note map read from the tree.
+ * @throws IOException
+ * the repository cannot be accessed through the reader.
+ * @throws CorruptObjectException
+ * a tree object is corrupt and cannot be read.
+ * @throws IncorrectObjectTypeException
+ * a tree object wasn't actually a tree.
+ * @throws MissingObjectException
+ * a reference tree object doesn't exist.
+ */
+ public static NoteMap read(ObjectReader reader, RevTree tree)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ CorruptObjectException, IOException {
+ return readTree(reader, tree);
+ }
+
+ /**
+ * Load a collection of notes from a tree.
+ *
+ * @param reader
+ * reader to scan the note branch with. This reader may be
+ * retained by the NoteMap for the life of the map in order to
+ * support lazy loading of entries.
+ * @param treeId
+ * the note tree to read.
+ * @return the note map read from the tree.
+ * @throws IOException
+ * the repository cannot be accessed through the reader.
+ * @throws CorruptObjectException
+ * a tree object is corrupt and cannot be read.
+ * @throws IncorrectObjectTypeException
+ * a tree object wasn't actually a tree.
+ * @throws MissingObjectException
+ * a reference tree object doesn't exist.
+ */
+ public static NoteMap readTree(ObjectReader reader, ObjectId treeId)
+ throws MissingObjectException, IncorrectObjectTypeException,
+ CorruptObjectException, IOException {
+ NoteMap map = new NoteMap(reader);
+ map.load(treeId);
+ return map;
+ }
+
+ /** Borrowed reader to access the repository. */
+ private final ObjectReader reader;
+
+ /** All of the notes that have been loaded. */
+ private ObjectIdSubclassMap<Note> notes = new ObjectIdSubclassMap<Note>();
+
+ private NoteMap(ObjectReader reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Lookup a note for a specific ObjectId.
+ *
+ * @param id
+ * the object to look for.
+ * @return the note's blob ObjectId, or null if no note exists.
+ */
+ public ObjectId get(AnyObjectId id) {
+ Note note = notes.get(id);
+ return note != null ? note.getData() : null;
+ }
+
+ /**
+ * Determine if a note exists for the specified ObjectId.
+ *
+ * @param id
+ * the object to look for.
+ * @return true if a note exists; false if there is no note.
+ */
+ public boolean contains(AnyObjectId id) {
+ return get(id) != null;
+ }
+
+ /**
+ * Open and return the content of an object's note.
+ *
+ * This method assumes the note is fairly small and can be accessed
+ * efficiently. Larger notes should be accessed by streaming:
+ *
+ * <pre>
+ * ObjectId dataId = thisMap.get(id);
+ * if (dataId != null)
+ * reader.open(dataId).openStream();
+ * </pre>
+ *
+ * @param id
+ * object to lookup the note of.
+ * @param sizeLimit
+ * maximum number of bytes to return. If the note data size is
+ * larger than this limit, LargeObjectException will be thrown.
+ * @return if a note is defined for {@code id}, the note content. If no note
+ * is defined, null.
+ * @throws LargeObjectException
+ * the note data is larger than {@code sizeLimit}.
+ * @throws MissingObjectException
+ * the note's blob does not exist in the repository.
+ * @throws IOException
+ * the note's blob cannot be read from the repository
+ */
+ public byte[] getCachedBytes(AnyObjectId id, int sizeLimit)
+ throws LargeObjectException, MissingObjectException, IOException {
+ ObjectId dataId = get(id);
+ if (dataId != null)
+ return reader.open(dataId).getCachedBytes(sizeLimit);
+ else
+ return null;
+ }
+
+ private void load(ObjectId treeId) throws MissingObjectException,
+ IncorrectObjectTypeException, CorruptObjectException, IOException {
+ try {
+ byte[] idBuf = new byte[Constants.OBJECT_ID_LENGTH];
+ TreeWalk tw = new TreeWalk(reader);
+ tw.reset();
+ tw.setRecursive(true);
+ tw.addTree(treeId);
+
+ notes.clear();
+
+ while (tw.next()) {
+ ObjectId pathId = pathToId(idBuf, tw.getRawPath());
+ if (pathId != null)
+ notes.add(new Note(pathId, tw.getObjectId(0)));
+ }
+ } finally {
+ reader.release();
+ }
+ }
+
+ private static ObjectId pathToId(byte[] rawIdBuf, byte[] path) {
+ int r = 0;
+
+ for (int i = 0; i < path.length; i++) {
+ byte c = path[i];
+
+ // Skip embedded '/' in paths, these aren't part of a name.
+ if (c == '/')
+ continue;
+
+ // We need at least 2 digits to consume in this cycle.
+ if (path.length < i + 2)
+ return null;
+
+ // We cannot exceed an ObjectId raw length.
+ if (r == Constants.OBJECT_ID_STRING_LENGTH)
+ return null;
+
+ int high, low;
+ try {
+ high = RawParseUtils.parseHexInt4(c);
+ low = RawParseUtils.parseHexInt4(path[++i]);
+ } catch (ArrayIndexOutOfBoundsException notHex) {
+ return null;
+ }
+
+ rawIdBuf[r++] = (byte) ((high << 4) | low);
+ }
+
+ // We need exactly the right number of input bytes.
+ if (r == Constants.OBJECT_ID_LENGTH)
+ return ObjectId.fromRaw(rawIdBuf);
+ else
+ return null;
+ }
+}