Merge "Allow writing a NoteMap back to the repository"
This commit is contained in:
commit
e9002a45ce
|
@ -183,6 +183,17 @@ public void tick(final int secDelta) {
|
|||
now += secDelta * 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the author and committer using {@link #getClock()}.
|
||||
*
|
||||
* @param c
|
||||
* the commit builder to store.
|
||||
*/
|
||||
public void setAuthorAndCommitter(org.eclipse.jgit.lib.CommitBuilder c) {
|
||||
c.setAuthor(new PersonIdent(author, new Date(now)));
|
||||
c.setCommitter(new PersonIdent(committer, new Date(now)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new blob object in the repository.
|
||||
*
|
||||
|
@ -815,8 +826,7 @@ public RevCommit create() throws Exception {
|
|||
|
||||
c = new org.eclipse.jgit.lib.CommitBuilder();
|
||||
c.setParentIds(parents);
|
||||
c.setAuthor(new PersonIdent(author, new Date(now)));
|
||||
c.setCommitter(new PersonIdent(committer, new Date(now)));
|
||||
setAuthorAndCommitter(c);
|
||||
c.setMessage(message);
|
||||
|
||||
ObjectId commitId;
|
||||
|
|
|
@ -43,7 +43,10 @@
|
|||
|
||||
package org.eclipse.jgit.notes;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.MutableObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectInserter;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
|
@ -51,6 +54,8 @@
|
|||
import org.eclipse.jgit.lib.RepositoryTestCase;
|
||||
import org.eclipse.jgit.revwalk.RevBlob;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.util.RawParseUtils;
|
||||
|
||||
public class NoteMapTest extends RepositoryTestCase {
|
||||
|
@ -188,6 +193,59 @@ public void testGetCachedBytes() throws Exception {
|
|||
assertEquals(exp, RawParseUtils.decode(act));
|
||||
}
|
||||
|
||||
public void testWriteUnchangedFlat() 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) //
|
||||
.add(".gitignore", "") //
|
||||
.add("zoo-animals.txt", "") //
|
||||
.create();
|
||||
tr.parseBody(r);
|
||||
|
||||
NoteMap map = NoteMap.read(reader, r);
|
||||
assertTrue("has note for a", map.contains(a));
|
||||
assertTrue("has note for b", map.contains(b));
|
||||
|
||||
RevCommit n = commitNoteMap(map);
|
||||
assertNotSame("is new commit", r, n);
|
||||
assertSame("same tree", r.getTree(), n.getTree());
|
||||
}
|
||||
|
||||
public void testWriteUnchangedFanout2_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) //
|
||||
.add(".gitignore", "") //
|
||||
.add("zoo-animals.txt", "") //
|
||||
.create();
|
||||
tr.parseBody(r);
|
||||
|
||||
NoteMap map = NoteMap.read(reader, r);
|
||||
assertTrue("has note for a", map.contains(a));
|
||||
assertTrue("has note for b", map.contains(b));
|
||||
|
||||
// This is a non-lazy map, so we'll be looking at the leaf buckets.
|
||||
RevCommit n = commitNoteMap(map);
|
||||
assertNotSame("is new commit", r, n);
|
||||
assertSame("same tree", r.getTree(), n.getTree());
|
||||
|
||||
// Use a lazy-map for the next round of the same test.
|
||||
map = NoteMap.read(reader, r);
|
||||
n = commitNoteMap(map);
|
||||
assertNotSame("is new commit", r, n);
|
||||
assertSame("same tree", r.getTree(), n.getTree());
|
||||
}
|
||||
|
||||
public void testCreateFromEmpty() throws Exception {
|
||||
RevBlob a = tr.blob("a");
|
||||
RevBlob b = tr.blob("b");
|
||||
|
@ -226,6 +284,8 @@ public void testEditFlat() throws Exception {
|
|||
RevCommit r = tr.commit() //
|
||||
.add(a.name(), data1) //
|
||||
.add(b.name(), data2) //
|
||||
.add(".gitignore", "") //
|
||||
.add("zoo-animals.txt", b) //
|
||||
.create();
|
||||
tr.parseBody(r);
|
||||
|
||||
|
@ -250,6 +310,15 @@ public void testEditFlat() throws Exception {
|
|||
id.setByte(1, p);
|
||||
assertTrue("contains " + id, map.contains(id));
|
||||
}
|
||||
|
||||
RevCommit n = commitNoteMap(map);
|
||||
map = NoteMap.read(reader, n);
|
||||
assertEquals(data2, map.get(a));
|
||||
assertEquals(b, map.get(data1));
|
||||
assertFalse("no b", map.contains(b));
|
||||
assertFalse("no data2", map.contains(data2));
|
||||
assertEquals(b, TreeWalk
|
||||
.forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
|
||||
}
|
||||
|
||||
public void testEditFanout2_38() throws Exception {
|
||||
|
@ -261,6 +330,8 @@ public void testEditFanout2_38() throws Exception {
|
|||
RevCommit r = tr.commit() //
|
||||
.add(fanout(2, a.name()), data1) //
|
||||
.add(fanout(2, b.name()), data2) //
|
||||
.add(".gitignore", "") //
|
||||
.add("zoo-animals.txt", b) //
|
||||
.create();
|
||||
tr.parseBody(r);
|
||||
|
||||
|
@ -274,11 +345,46 @@ public void testEditFanout2_38() throws Exception {
|
|||
assertEquals(b, map.get(data1));
|
||||
assertFalse("no b", map.contains(b));
|
||||
assertFalse("no data2", map.contains(data2));
|
||||
RevCommit n = commitNoteMap(map);
|
||||
|
||||
map.set(a, null);
|
||||
map.set(data1, null);
|
||||
assertFalse("no a", map.contains(a));
|
||||
assertFalse("no data1", map.contains(data1));
|
||||
|
||||
map = NoteMap.read(reader, n);
|
||||
assertEquals(data2, map.get(a));
|
||||
assertEquals(b, map.get(data1));
|
||||
assertFalse("no b", map.contains(b));
|
||||
assertFalse("no data2", map.contains(data2));
|
||||
assertEquals(b, TreeWalk
|
||||
.forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
|
||||
}
|
||||
|
||||
public void testRemoveDeletesTreeFanout2_38() throws Exception {
|
||||
RevBlob a = tr.blob("a");
|
||||
RevBlob data1 = tr.blob("data1");
|
||||
RevTree empty = tr.tree();
|
||||
|
||||
RevCommit r = tr.commit() //
|
||||
.add(fanout(2, a.name()), data1) //
|
||||
.create();
|
||||
tr.parseBody(r);
|
||||
|
||||
NoteMap map = NoteMap.read(reader, r);
|
||||
map.set(a, null);
|
||||
|
||||
RevCommit n = commitNoteMap(map);
|
||||
assertEquals("empty tree", empty, n.getTree());
|
||||
}
|
||||
|
||||
private RevCommit commitNoteMap(NoteMap map) throws IOException {
|
||||
tr.tick(600);
|
||||
|
||||
CommitBuilder builder = new CommitBuilder();
|
||||
builder.setTreeId(map.writeTree(inserter));
|
||||
tr.setAuthorAndCommitter(builder);
|
||||
return tr.getRevWalk().parseCommit(inserter.insert(builder));
|
||||
}
|
||||
|
||||
private static String fanout(int prefix, String name) {
|
||||
|
|
|
@ -43,12 +43,16 @@
|
|||
|
||||
package org.eclipse.jgit.notes;
|
||||
|
||||
import static org.eclipse.jgit.lib.FileMode.TREE;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jgit.lib.AbbreviatedObjectId;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectInserter;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
import org.eclipse.jgit.lib.TreeFormatter;
|
||||
|
||||
/**
|
||||
* A note tree holding only note subtrees, each named using a 2 digit hex name.
|
||||
|
@ -136,6 +140,43 @@ InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
|
|||
}
|
||||
}
|
||||
|
||||
private static final byte[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
|
||||
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
|
||||
@Override
|
||||
ObjectId writeTree(ObjectInserter inserter) throws IOException {
|
||||
byte[] nameBuf = new byte[2];
|
||||
TreeFormatter fmt = new TreeFormatter(treeSize());
|
||||
NonNoteEntry e = nonNotes;
|
||||
|
||||
for (int cell = 0; cell < 256; cell++) {
|
||||
NoteBucket b = table[cell];
|
||||
if (b == null)
|
||||
continue;
|
||||
|
||||
nameBuf[0] = hexchar[cell >>> 4];
|
||||
nameBuf[1] = hexchar[cell & 0x0f];
|
||||
|
||||
while (e != null && e.pathCompare(nameBuf, 0, 2, TREE) < 0) {
|
||||
e.format(fmt);
|
||||
e = e.next;
|
||||
}
|
||||
|
||||
fmt.append(nameBuf, 0, 2, TREE, b.writeTree(inserter));
|
||||
}
|
||||
|
||||
for (; e != null; e = e.next)
|
||||
e.format(fmt);
|
||||
return fmt.insert(inserter);
|
||||
}
|
||||
|
||||
private int treeSize() {
|
||||
int sz = cnt * TreeFormatter.entrySize(TREE, 2);
|
||||
for (NonNoteEntry e = nonNotes; e != null; e = e.next)
|
||||
sz += e.treeEntrySize();
|
||||
return sz;
|
||||
}
|
||||
|
||||
private int cell(AnyObjectId id) {
|
||||
return id.getByte(prefixLen >> 1);
|
||||
}
|
||||
|
@ -158,6 +199,11 @@ InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
|
|||
return load(noteOn, or).set(noteOn, noteData, or);
|
||||
}
|
||||
|
||||
@Override
|
||||
ObjectId writeTree(ObjectInserter inserter) {
|
||||
return treeId;
|
||||
}
|
||||
|
||||
private NoteBucket load(AnyObjectId objId, ObjectReader or)
|
||||
throws IOException {
|
||||
AbbreviatedObjectId p = objId.abbreviate(prefixLen + 2);
|
||||
|
|
|
@ -43,11 +43,16 @@
|
|||
|
||||
package org.eclipse.jgit.notes;
|
||||
|
||||
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
|
||||
import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectInserter;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
import org.eclipse.jgit.lib.TreeFormatter;
|
||||
|
||||
/**
|
||||
* A note tree holding only notes, with no subtrees.
|
||||
|
@ -126,6 +131,39 @@ InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
ObjectId writeTree(ObjectInserter inserter) throws IOException {
|
||||
byte[] nameBuf = new byte[OBJECT_ID_STRING_LENGTH];
|
||||
int nameLen = OBJECT_ID_STRING_LENGTH - prefixLen;
|
||||
TreeFormatter fmt = new TreeFormatter(treeSize(nameLen));
|
||||
NonNoteEntry e = nonNotes;
|
||||
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
Note n = notes[i];
|
||||
|
||||
n.copyTo(nameBuf, 0);
|
||||
|
||||
while (e != null
|
||||
&& e.pathCompare(nameBuf, prefixLen, nameLen, REGULAR_FILE) < 0) {
|
||||
e.format(fmt);
|
||||
e = e.next;
|
||||
}
|
||||
|
||||
fmt.append(nameBuf, prefixLen, nameLen, REGULAR_FILE, n.getData());
|
||||
}
|
||||
|
||||
for (; e != null; e = e.next)
|
||||
e.format(fmt);
|
||||
return fmt.insert(inserter);
|
||||
}
|
||||
|
||||
private int treeSize(final int nameLen) {
|
||||
int sz = cnt * TreeFormatter.entrySize(REGULAR_FILE, nameLen);
|
||||
for (NonNoteEntry e = nonNotes; e != null; e = e.next)
|
||||
sz += e.treeEntrySize();
|
||||
return sz;
|
||||
}
|
||||
|
||||
void parseOneEntry(AnyObjectId noteOn, AnyObjectId noteData) {
|
||||
growIfFull();
|
||||
notes[cnt++] = new Note(noteOn, noteData.copy());
|
||||
|
|
|
@ -68,4 +68,33 @@ class NonNoteEntry extends ObjectId {
|
|||
void format(TreeFormatter fmt) {
|
||||
fmt.append(name, mode, this);
|
||||
}
|
||||
|
||||
int treeEntrySize() {
|
||||
return TreeFormatter.entrySize(mode, name.length);
|
||||
}
|
||||
|
||||
int pathCompare(byte[] bBuf, int bPos, int bLen, FileMode bMode) {
|
||||
return pathCompare(name, 0, name.length, mode, //
|
||||
bBuf, bPos, bLen, bMode);
|
||||
}
|
||||
|
||||
private static int pathCompare(final byte[] aBuf, int aPos, final int aEnd,
|
||||
final FileMode aMode, final byte[] bBuf, int bPos, final int bEnd,
|
||||
final FileMode bMode) {
|
||||
while (aPos < aEnd && bPos < bEnd) {
|
||||
int cmp = (aBuf[aPos++] & 0xff) - (bBuf[bPos++] & 0xff);
|
||||
if (cmp != 0)
|
||||
return cmp;
|
||||
}
|
||||
|
||||
if (aPos < aEnd)
|
||||
return (aBuf[aPos] & 0xff) - lastPathChar(bMode);
|
||||
if (bPos < bEnd)
|
||||
return lastPathChar(aMode) - (bBuf[bPos] & 0xff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int lastPathChar(final FileMode mode) {
|
||||
return FileMode.TREE.equals(mode.getBits()) ? '/' : '\0';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectInserter;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
|
||||
/**
|
||||
|
@ -61,4 +62,6 @@ abstract ObjectId get(AnyObjectId objId, ObjectReader reader)
|
|||
|
||||
abstract InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
|
||||
ObjectReader reader) throws IOException;
|
||||
|
||||
abstract ObjectId writeTree(ObjectInserter inserter) throws IOException;
|
||||
}
|
||||
|
|
|
@ -309,6 +309,21 @@ public void remove(AnyObjectId noteOn) throws IOException {
|
|||
set(noteOn, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write this note map as a tree.
|
||||
*
|
||||
* @param inserter
|
||||
* inserter to use when writing trees to the object database.
|
||||
* Caller is responsible for flushing the inserter before trying
|
||||
* to read the objects, or exposing them through a reference.
|
||||
* @return the top level tree.
|
||||
* @throws IOException
|
||||
* a tree could not be written.
|
||||
*/
|
||||
public ObjectId writeTree(ObjectInserter inserter) throws IOException {
|
||||
return root.writeTree(inserter);
|
||||
}
|
||||
|
||||
private void load(ObjectId rootTree) throws MissingObjectException,
|
||||
IncorrectObjectTypeException, CorruptObjectException, IOException {
|
||||
AbbreviatedObjectId none = AbbreviatedObjectId.fromString("");
|
||||
|
|
Loading…
Reference in New Issue