Merge "Fix null ref exception in DirCacheCheckout"
diff --git a/org.eclipse.jgit.console/META-INF/MANIFEST.MF b/org.eclipse.jgit.console/META-INF/MANIFEST.MF
index a29b348..9607bdc 100644
--- a/org.eclipse.jgit.console/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.console/META-INF/MANIFEST.MF
@@ -7,7 +7,8 @@
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: JavaSE-1.6
 Export-Package: org.eclipse.jgit.console;version="0.10.0"
-Import-Package: org.eclipse.jgit.nls;version="[0.10.0,0.11.0)",
+Import-Package: org.eclipse.jgit.errors;version="[0.10.0,0.11.0)",
+ org.eclipse.jgit.nls;version="[0.10.0,0.11.0)",
  org.eclipse.jgit.transport;version="[0.10.0,0.11.0)",
  org.eclipse.jgit.util;version="[0.10.0,0.11.0)"
 Require-Bundle: com.jcraft.jsch;bundle-version="[0.1.37,0.2.0)"
diff --git a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java b/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
new file mode 100644
index 0000000..f8f8ff9
--- /dev/null
+++ b/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleCredentialsProvider.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * 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.console;
+
+import java.io.Console;
+
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+
+/** Interacts with the user during authentication by using the text console. */
+public class ConsoleCredentialsProvider extends CredentialsProvider {
+	/** Install this implementation as the default. */
+	public static void install() {
+		final ConsoleCredentialsProvider c = new ConsoleCredentialsProvider();
+		if (c.cons == null)
+			throw new NoClassDefFoundError(
+					ConsoleText.get().noSystemConsoleAvailable);
+		CredentialsProvider.setDefault(c);
+	}
+
+	private final Console cons = System.console();
+
+	@Override
+	public boolean isInteractive() {
+		return true;
+	}
+
+	@Override
+	public boolean supports(CredentialItem... items) {
+		for (CredentialItem i : items) {
+			if (i instanceof CredentialItem.StringType)
+				continue;
+
+			else if (i instanceof CredentialItem.CharArrayType)
+				continue;
+
+			else if (i instanceof CredentialItem.YesNoType)
+				continue;
+
+			else if (i instanceof CredentialItem.InformationalMessage)
+				continue;
+
+			else
+				return false;
+		}
+		return true;
+	}
+
+	@Override
+	public boolean get(URIish uri, CredentialItem... items)
+			throws UnsupportedCredentialItem {
+		boolean ok = true;
+		for (int i = 0; i < items.length && ok; i++) {
+			CredentialItem item = items[i];
+
+			if (item instanceof CredentialItem.StringType)
+				ok = get(uri, (CredentialItem.StringType) item);
+
+			else if (item instanceof CredentialItem.CharArrayType)
+				ok = get(uri, (CredentialItem.CharArrayType) item);
+
+			else if (item instanceof CredentialItem.YesNoType)
+				ok = get(uri, (CredentialItem.YesNoType) item);
+
+			else if (item instanceof CredentialItem.InformationalMessage)
+				ok = get(uri, (CredentialItem.InformationalMessage) item);
+
+			else
+				throw new UnsupportedCredentialItem(uri, item.getPromptText());
+		}
+		return ok;
+	}
+
+	private boolean get(URIish uri, CredentialItem.StringType item) {
+		if (item.isValueSecure()) {
+			char[] v = cons.readPassword("%s: ", item.getPromptText());
+			if (v != null) {
+				item.setValue(new String(v));
+				return true;
+			} else {
+				return false;
+			}
+		} else {
+			String v = cons.readLine("%s: ", item.getPromptText());
+			if (v != null) {
+				item.setValue(v);
+				return true;
+			} else {
+				return false;
+			}
+		}
+	}
+
+	private boolean get(URIish uri, CredentialItem.CharArrayType item) {
+		if (item.isValueSecure()) {
+			char[] v = cons.readPassword("%s: ", item.getPromptText());
+			if (v != null) {
+				item.setValueNoCopy(v);
+				return true;
+			} else {
+				return false;
+			}
+		} else {
+			String v = cons.readLine("%s: ", item.getPromptText());
+			if (v != null) {
+				item.setValueNoCopy(v.toCharArray());
+				return true;
+			} else {
+				return false;
+			}
+		}
+	}
+
+	private boolean get(URIish uri, CredentialItem.InformationalMessage item) {
+		cons.printf("%s\n", item.getPromptText());
+		cons.flush();
+		return true;
+	}
+
+	private boolean get(URIish uri, CredentialItem.YesNoType item) {
+		String r = cons.readLine("%s [%s/%s]? ", item.getPromptText(),
+				ConsoleText.get().answerYes, ConsoleText.get().answerNo);
+		if (r != null) {
+			item.setValue(ConsoleText.get().answerYes.equalsIgnoreCase(r));
+			return true;
+		} else {
+			return false;
+		}
+	}
+}
diff --git a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleSshSessionFactory.java b/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleSshSessionFactory.java
deleted file mode 100644
index b11078d..0000000
--- a/org.eclipse.jgit.console/src/org/eclipse/jgit/console/ConsoleSshSessionFactory.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2009, Google Inc.
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * 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.console;
-
-import java.io.Console;
-
-import org.eclipse.jgit.transport.OpenSshConfig;
-import org.eclipse.jgit.transport.SshConfigSessionFactory;
-import org.eclipse.jgit.transport.SshSessionFactory;
-
-import com.jcraft.jsch.Session;
-import com.jcraft.jsch.UIKeyboardInteractive;
-import com.jcraft.jsch.UserInfo;
-
-/**
- * Loads known hosts and private keys from <code>$HOME/.ssh</code>.
- * <p>
- * This is the default implementation used by JGit and provides most of the
- * compatibility necessary to match OpenSSH, a popular implementation of SSH
- * used by C Git.
- * <p>
- * If user interactivity is required by SSH (e.g. to obtain a password) the
- * system console is used to display a prompt to the end-user.
- */
-public class ConsoleSshSessionFactory extends SshConfigSessionFactory {
-	/** Install this session factory implementation into the JVM. */
-	public static void install() {
-		final ConsoleSshSessionFactory c = new ConsoleSshSessionFactory();
-		if (c.cons == null)
-			throw new NoClassDefFoundError(ConsoleText.get().noSystemConsoleAvailable);
-		SshSessionFactory.setInstance(c);
-	}
-
-	private final Console cons = System.console();
-
-	@Override
-	protected void configure(final OpenSshConfig.Host hc, final Session session) {
-		if (!hc.isBatchMode())
-			session.setUserInfo(new ConsoleUserInfo());
-	}
-
-	private class ConsoleUserInfo implements UserInfo, UIKeyboardInteractive {
-		private String passwd;
-
-		private String passphrase;
-
-		public void showMessage(final String msg) {
-			cons.printf("%s\n", msg);
-			cons.flush();
-		}
-
-		public boolean promptYesNo(final String msg) {
-			String r = cons.readLine("%s [%s/%s]? ", msg, ConsoleText.get().answerYes, ConsoleText.get().answerNo);
-			return ConsoleText.get().answerYes.equalsIgnoreCase(r);
-		}
-
-		public boolean promptPassword(final String msg) {
-			passwd = null;
-			char[] p = cons.readPassword("%s: ", msg);
-			if (p != null) {
-				passwd = new String(p);
-				return true;
-			}
-			return false;
-		}
-
-		public boolean promptPassphrase(final String msg) {
-			passphrase = null;
-			char[] p = cons.readPassword("%s: ", msg);
-			if (p != null) {
-				passphrase = new String(p);
-				return true;
-			}
-			return false;
-		}
-
-		public String getPassword() {
-			return passwd;
-		}
-
-		public String getPassphrase() {
-			return passphrase;
-		}
-
-		public String[] promptKeyboardInteractive(final String destination,
-				final String name, final String instruction,
-				final String[] prompt, final boolean[] echo) {
-			cons.printf("%s: %s\n", destination, name);
-			cons.printf("%s\n", instruction);
-			final String[] response = new String[prompt.length];
-			for (int i = 0; i < prompt.length; i++) {
-				if (echo[i]) {
-					response[i] = cons.readLine("%s: ", prompt[i]);
-				} else {
-					final char[] p = cons.readPassword("%s: ", prompt[i]);
-					response[i] = p != null ? new String(p) : "";
-				}
-			}
-			return response;
-		}
-	}
-}
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
index 06a4eb6..92584cb 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/HttpClientTests.java
@@ -60,6 +60,7 @@
 import org.eclipse.jgit.http.server.resolver.RepositoryResolver;
 import org.eclipse.jgit.http.server.resolver.ServiceNotEnabledException;
 import org.eclipse.jgit.http.test.util.AccessEvent;
+import org.eclipse.jgit.http.test.util.AppServer;
 import org.eclipse.jgit.http.test.util.HttpTestCase;
 import org.eclipse.jgit.junit.TestRepository;
 import org.eclipse.jgit.lib.Constants;
@@ -71,6 +72,7 @@
 import org.eclipse.jgit.transport.FetchConnection;
 import org.eclipse.jgit.transport.Transport;
 import org.eclipse.jgit.transport.URIish;
+import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
 
 public class HttpClientTests extends HttpTestCase {
 	private TestRepository<FileRepository> remoteRepository;
@@ -282,7 +284,7 @@ public void testListRemote_Dumb_NeedsAuth() throws Exception {
 				fail("connection opened even info/refs needs auth basic");
 			} catch (TransportException err) {
 				String exp = dumbAuthBasicURI + ": "
-						+ JGitText.get().authenticationNotSupported;
+						+ JGitText.get().notAuthorized;
 				assertEquals(exp, err.getMessage());
 			}
 		} finally {
@@ -290,6 +292,31 @@ public void testListRemote_Dumb_NeedsAuth() throws Exception {
 		}
 	}
 
+	public void testListRemote_Dumb_Auth() throws Exception {
+		Repository dst = createBareRepository();
+		Transport t = Transport.open(dst, dumbAuthBasicURI);
+		t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
+				AppServer.username, AppServer.password));
+		try {
+			t.openFetch();
+		} finally {
+			t.close();
+		}
+		t = Transport.open(dst, dumbAuthBasicURI);
+		t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
+				AppServer.username, ""));
+		try {
+			t.openFetch();
+			fail("connection opened even info/refs needs auth basic and we provide wrong password");
+		} catch (TransportException err) {
+			String exp = dumbAuthBasicURI + ": "
+					+ JGitText.get().notAuthorized;
+			assertEquals(exp, err.getMessage());
+		} finally {
+			t.close();
+		}
+	}
+
 	public void testListRemote_Smart_UploadPackNeedsAuth() throws Exception {
 		Repository dst = createBareRepository();
 		Transport t = Transport.open(dst, smartAuthBasicURI);
@@ -299,7 +326,7 @@ public void testListRemote_Smart_UploadPackNeedsAuth() throws Exception {
 				fail("connection opened even though service disabled");
 			} catch (TransportException err) {
 				String exp = smartAuthBasicURI + ": "
-						+ JGitText.get().authenticationNotSupported;
+						+ JGitText.get().notAuthorized;
 				assertEquals(exp, err.getMessage());
 			}
 		} finally {
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
index ca3960e..62ffd8d 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/Main.java
@@ -55,7 +55,7 @@
 import java.util.List;
 
 import org.eclipse.jgit.awtui.AwtAuthenticator;
-import org.eclipse.jgit.awtui.AwtSshSessionFactory;
+import org.eclipse.jgit.awtui.AwtCredentialsProvider;
 import org.eclipse.jgit.errors.TransportException;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.RepositoryBuilder;
@@ -114,7 +114,7 @@ protected void run(final String[] argv) {
 		try {
 			if (!installConsole()) {
 				AwtAuthenticator.install();
-				AwtSshSessionFactory.install();
+				AwtCredentialsProvider.install();
 			}
 			configureHttpProxy();
 			execute(argv);
@@ -218,7 +218,7 @@ protected Repository openGitDir(String gitdir) throws IOException {
 	private static boolean installConsole() {
 		try {
 			install("org.eclipse.jgit.console.ConsoleAuthenticator");
-			install("org.eclipse.jgit.console.ConsoleSshSessionFactory");
+			install("org.eclipse.jgit.console.ConsoleCredentialsProvider");
 			return true;
 		} catch (ClassNotFoundException e) {
 			return false;
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
index 8d85084..0a87fd3 100644
--- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF
@@ -26,6 +26,7 @@
  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)",
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_ObjectId.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
similarity index 76%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_ObjectId.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
index 03176cb..e5faff1 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/T0001_ObjectId.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdTest.java
@@ -47,7 +47,7 @@
 
 import junit.framework.TestCase;
 
-public class T0001_ObjectId extends TestCase {
+public class ObjectIdTest extends TestCase {
 	public void test001_toString() {
 		final String x = "def4c620bc3713bb1bb26b808ec9312548e73946";
 		final ObjectId oid = ObjectId.fromString(x);
@@ -108,4 +108,47 @@ public void test011_toString() {
 		final ObjectId oid = ObjectId.fromString(x);
 		assertEquals(x.toLowerCase(), oid.name());
 	}
+
+	public void testGetByte() {
+		byte[] raw = new byte[20];
+		for (int i = 0; i < 20; i++)
+			raw[i] = (byte) (0xa0 + i);
+		ObjectId id = ObjectId.fromRaw(raw);
+
+		assertEquals(raw[0] & 0xff, id.getFirstByte());
+		assertEquals(raw[0] & 0xff, id.getByte(0));
+		assertEquals(raw[1] & 0xff, id.getByte(1));
+
+		for (int i = 2; i < 20; i++)
+			assertEquals("index " + i, raw[i] & 0xff, id.getByte(i));
+	}
+
+	public void testSetByte() {
+		byte[] exp = new byte[20];
+		for (int i = 0; i < 20; i++)
+			exp[i] = (byte) (0xa0 + i);
+
+		MutableObjectId id = new MutableObjectId();
+		id.fromRaw(exp);
+		assertEquals(ObjectId.fromRaw(exp).name(), id.name());
+
+		id.setByte(0, 0x10);
+		assertEquals(0x10, id.getByte(0));
+		exp[0] = 0x10;
+		assertEquals(ObjectId.fromRaw(exp).name(), id.name());
+
+		for (int p = 1; p < 20; p++) {
+			id.setByte(p, 0x10 + p);
+			assertEquals(0x10 + p, id.getByte(p));
+			exp[p] = (byte) (0x10 + p);
+			assertEquals(ObjectId.fromRaw(exp).name(), id.name());
+		}
+
+		for (int p = 0; p < 20; p++) {
+			id.setByte(p, 0x80 + p);
+			assertEquals(0x80 + p, id.getByte(p));
+			exp[p] = (byte) (0x80 + p);
+			assertEquals(ObjectId.fromRaw(exp).name(), id.name());
+		}
+	}
 }
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
new file mode 100644
index 0000000..786f1b9
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/notes/NoteMapTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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();
+	}
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
index 2d3ec7b..0ed336d 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/storage/file/PackIndexV2Test.java
@@ -74,21 +74,21 @@ public File getFileForPackdf2982f28() {
 	public void testCRC32() throws MissingObjectException,
 			UnsupportedOperationException {
 		assertTrue(smallIdx.hasCRC32Support());
-		assertEquals(0x00000000C2B64258l, smallIdx.findCRC32(ObjectId
+		assertEquals(0x00000000C2B64258L, smallIdx.findCRC32(ObjectId
 				.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")));
-		assertEquals(0x0000000072AD57C2l, smallIdx.findCRC32(ObjectId
+		assertEquals(0x0000000072AD57C2L, smallIdx.findCRC32(ObjectId
 				.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab")));
-		assertEquals(0x00000000FF10A479l, smallIdx.findCRC32(ObjectId
+		assertEquals(0x00000000FF10A479L, smallIdx.findCRC32(ObjectId
 				.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259")));
-		assertEquals(0x0000000034B27DDCl, smallIdx.findCRC32(ObjectId
+		assertEquals(0x0000000034B27DDCL, smallIdx.findCRC32(ObjectId
 				.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3")));
-		assertEquals(0x000000004743F1E4l, smallIdx.findCRC32(ObjectId
+		assertEquals(0x000000004743F1E4L, smallIdx.findCRC32(ObjectId
 				.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
-		assertEquals(0x00000000640B358Bl, smallIdx.findCRC32(ObjectId
+		assertEquals(0x00000000640B358BL, smallIdx.findCRC32(ObjectId
 				.fromString("902d5476fa249b7abc9d84c611577a81381f0327")));
-		assertEquals(0x000000002A17CB5El, smallIdx.findCRC32(ObjectId
+		assertEquals(0x000000002A17CB5EL, smallIdx.findCRC32(ObjectId
 				.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035")));
-		assertEquals(0x000000000B3B5BA6l, smallIdx.findCRC32(ObjectId
+		assertEquals(0x000000000B3B5BA6L, smallIdx.findCRC32(ObjectId
 				.fromString("c59759f143fb1fe21c197981df75a7ee00290799")));
 	}
 }
diff --git a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
index a3afa67..36fbae9 100644
--- a/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit.ui/META-INF/MANIFEST.MF
@@ -7,7 +7,8 @@
 Bundle-Vendor: %provider_name
 Bundle-RequiredExecutionEnvironment: J2SE-1.5
 Export-Package: org.eclipse.jgit.awtui;version="0.10.0"
-Import-Package: org.eclipse.jgit.lib;version="[0.10.0,0.11.0)",
+Import-Package: org.eclipse.jgit.errors;version="[0.10.0,0.11.0)", 
+ org.eclipse.jgit.lib;version="[0.10.0,0.11.0)",
  org.eclipse.jgit.nls;version="[0.10.0,0.11.0)",
  org.eclipse.jgit.revplot;version="[0.10.0,0.11.0)",
  org.eclipse.jgit.revwalk;version="[0.10.0,0.11.0)",
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtCredentialsProvider.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtCredentialsProvider.java
new file mode 100644
index 0000000..0e6f78a
--- /dev/null
+++ b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtCredentialsProvider.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * 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.awtui;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+
+/** Interacts with the user during authentication by using AWT/Swing dialogs. */
+public class AwtCredentialsProvider extends CredentialsProvider {
+	/** Install this implementation as the default. */
+	public static void install() {
+		CredentialsProvider.setDefault(new AwtCredentialsProvider());
+	}
+
+	@Override
+	public boolean isInteractive() {
+		return true;
+	}
+
+	@Override
+	public boolean supports(CredentialItem... items) {
+		for (CredentialItem i : items) {
+			if (i instanceof CredentialItem.StringType)
+				continue;
+
+			else if (i instanceof CredentialItem.CharArrayType)
+				continue;
+
+			else if (i instanceof CredentialItem.YesNoType)
+				continue;
+
+			else if (i instanceof CredentialItem.InformationalMessage)
+				continue;
+
+			else
+				return false;
+		}
+		return true;
+	}
+
+	@Override
+	public boolean get(URIish uri, CredentialItem... items)
+			throws UnsupportedCredentialItem {
+		if (items.length == 0) {
+			return true;
+
+		} else if (items.length == 1) {
+			final CredentialItem item = items[0];
+
+			if (item instanceof CredentialItem.InformationalMessage) {
+				JOptionPane.showMessageDialog(null, item.getPromptText(),
+						UIText.get().warning, JOptionPane.INFORMATION_MESSAGE);
+				return true;
+
+			} else if (item instanceof CredentialItem.YesNoType) {
+				CredentialItem.YesNoType v = (CredentialItem.YesNoType) item;
+				int r = JOptionPane.showConfirmDialog(null, v.getPromptText(),
+						UIText.get().warning, JOptionPane.YES_NO_OPTION);
+				switch (r) {
+				case JOptionPane.YES_OPTION:
+					v.setValue(true);
+					return true;
+
+				case JOptionPane.NO_OPTION:
+					v.setValue(false);
+					return true;
+
+				case JOptionPane.CANCEL_OPTION:
+				case JOptionPane.CLOSED_OPTION:
+				default:
+					return false;
+				}
+
+			} else {
+				return interactive(uri, items);
+			}
+
+		} else {
+			return interactive(uri, items);
+		}
+	}
+
+	private boolean interactive(URIish uri, CredentialItem[] items) {
+		final GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1,
+				GridBagConstraints.NORTHWEST, GridBagConstraints.NONE,
+				new Insets(0, 0, 0, 0), 0, 0);
+		final JPanel panel = new JPanel();
+		panel.setLayout(new GridBagLayout());
+
+		final JTextField[] texts = new JTextField[items.length];
+		for (int i = 0; i < items.length; i++) {
+			CredentialItem item = items[i];
+
+			if (item instanceof CredentialItem.StringType
+					|| item instanceof CredentialItem.CharArrayType) {
+				gbc.fill = GridBagConstraints.NONE;
+				gbc.gridwidth = GridBagConstraints.RELATIVE;
+				gbc.gridx = 0;
+				panel.add(new JLabel(item.getPromptText()), gbc);
+
+				gbc.fill = GridBagConstraints.HORIZONTAL;
+				gbc.gridwidth = GridBagConstraints.RELATIVE;
+				gbc.gridx = 1;
+				if (item.isValueSecure())
+					texts[i] = new JPasswordField(20);
+				else
+					texts[i] = new JTextField(20);
+				panel.add(texts[i], gbc);
+				gbc.gridy++;
+
+			} else if (item instanceof CredentialItem.InformationalMessage) {
+				gbc.fill = GridBagConstraints.NONE;
+				gbc.gridwidth = GridBagConstraints.REMAINDER;
+				gbc.gridx = 0;
+				panel.add(new JLabel(item.getPromptText()), gbc);
+				gbc.gridy++;
+
+			} else {
+				throw new UnsupportedCredentialItem(uri, item.getPromptText());
+			}
+		}
+
+		if (JOptionPane.showConfirmDialog(null, panel,
+				UIText.get().authenticationRequired,
+				JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.OK_OPTION)
+			return false; // cancel
+
+		for (int i = 0; i < items.length; i++) {
+			CredentialItem item = items[i];
+			JTextField f = texts[i];
+
+			if (item instanceof CredentialItem.StringType) {
+				CredentialItem.StringType v = (CredentialItem.StringType) item;
+				if (f instanceof JPasswordField)
+					v.setValue(new String(((JPasswordField) f).getPassword()));
+				else
+					v.setValue(f.getText());
+
+			} else if (item instanceof CredentialItem.CharArrayType) {
+				CredentialItem.CharArrayType v = (CredentialItem.CharArrayType) item;
+				if (f instanceof JPasswordField)
+					v.setValueNoCopy(((JPasswordField) f).getPassword());
+				else
+					v.setValueNoCopy(f.getText().toCharArray());
+			}
+		}
+		return true;
+	}
+}
diff --git a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtSshSessionFactory.java b/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtSshSessionFactory.java
deleted file mode 100644
index f0de7ce..0000000
--- a/org.eclipse.jgit.ui/src/org/eclipse/jgit/awtui/AwtSshSessionFactory.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * 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.awtui;
-
-import java.awt.Container;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
-
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JPasswordField;
-import javax.swing.JTextField;
-
-import org.eclipse.jgit.transport.OpenSshConfig;
-import org.eclipse.jgit.transport.SshConfigSessionFactory;
-import org.eclipse.jgit.transport.SshSessionFactory;
-
-import com.jcraft.jsch.Session;
-import com.jcraft.jsch.UIKeyboardInteractive;
-import com.jcraft.jsch.UserInfo;
-
-/**
- * Loads known hosts and private keys from <code>$HOME/.ssh</code>.
- * <p>
- * This is the default implementation used by JGit and provides most of the
- * compatibility necessary to match OpenSSH, a popular implementation of SSH
- * used by C Git.
- * <p>
- * If user interactivity is required by SSH (e.g. to obtain a password) AWT is
- * used to display a password input field to the end-user.
- */
-public class AwtSshSessionFactory extends SshConfigSessionFactory {
-	/** Install this session factory implementation into the JVM. */
-	public static void install() {
-		SshSessionFactory.setInstance(new AwtSshSessionFactory());
-	}
-
-	@Override
-	protected void configure(final OpenSshConfig.Host hc, final Session session) {
-		if (!hc.isBatchMode())
-			session.setUserInfo(new AWT_UserInfo());
-	}
-
-	private static class AWT_UserInfo implements UserInfo,
-			UIKeyboardInteractive {
-		private String passwd;
-
-		private String passphrase;
-
-		public void showMessage(final String msg) {
-			JOptionPane.showMessageDialog(null, msg);
-		}
-
-		public boolean promptYesNo(final String msg) {
-			return JOptionPane.showConfirmDialog(null, msg, UIText.get().warning,
-					JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION;
-		}
-
-		public boolean promptPassword(final String msg) {
-			passwd = null;
-			final JPasswordField passwordField = new JPasswordField(20);
-			final int result = JOptionPane.showConfirmDialog(null,
-					new Object[] { passwordField }, msg,
-					JOptionPane.OK_CANCEL_OPTION);
-			if (result == JOptionPane.OK_OPTION) {
-				passwd = new String(passwordField.getPassword());
-				return true;
-			}
-			return false;
-		}
-
-		public boolean promptPassphrase(final String msg) {
-			passphrase = null;
-			final JPasswordField passwordField = new JPasswordField(20);
-			final int result = JOptionPane.showConfirmDialog(null,
-					new Object[] { passwordField }, msg,
-					JOptionPane.OK_CANCEL_OPTION);
-			if (result == JOptionPane.OK_OPTION) {
-				passphrase = new String(passwordField.getPassword());
-				return true;
-			}
-			return false;
-		}
-
-		public String getPassword() {
-			return passwd;
-		}
-
-		public String getPassphrase() {
-			return passphrase;
-		}
-
-		public String[] promptKeyboardInteractive(final String destination,
-				final String name, final String instruction,
-				final String[] prompt, final boolean[] echo) {
-			final GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1,
-					1, 1, GridBagConstraints.NORTHWEST,
-					GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
-			final Container panel = new JPanel();
-			panel.setLayout(new GridBagLayout());
-
-			gbc.weightx = 1.0;
-			gbc.gridwidth = GridBagConstraints.REMAINDER;
-			gbc.gridx = 0;
-			panel.add(new JLabel(instruction), gbc);
-			gbc.gridy++;
-
-			gbc.gridwidth = GridBagConstraints.RELATIVE;
-
-			final JTextField[] texts = new JTextField[prompt.length];
-			for (int i = 0; i < prompt.length; i++) {
-				gbc.fill = GridBagConstraints.NONE;
-				gbc.gridx = 0;
-				gbc.weightx = 1;
-				panel.add(new JLabel(prompt[i]), gbc);
-
-				gbc.gridx = 1;
-				gbc.fill = GridBagConstraints.HORIZONTAL;
-				gbc.weighty = 1;
-				if (echo[i]) {
-					texts[i] = new JTextField(20);
-				} else {
-					texts[i] = new JPasswordField(20);
-				}
-				panel.add(texts[i], gbc);
-				gbc.gridy++;
-			}
-
-			if (JOptionPane.showConfirmDialog(null, panel, destination + ": "
-					+ name, JOptionPane.OK_CANCEL_OPTION,
-					JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) {
-				String[] response = new String[prompt.length];
-				for (int i = 0; i < prompt.length; i++) {
-					response[i] = texts[i].getText();
-				}
-				return response;
-			}
-			return null; // cancel
-		}
-	}
-}
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF
index 9e7362f..ba09e58 100644
--- a/org.eclipse.jgit/META-INF/MANIFEST.MF
+++ b/org.eclipse.jgit/META-INF/MANIFEST.MF
@@ -17,6 +17,7 @@
  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",
diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
index 8af6e31..ab4ec61 100644
--- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
+++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
@@ -124,7 +124,8 @@
 countingObjects=Counting objects
 createBranchFailedUnknownReason=Create branch failed for unknown reason
 createBranchUnexpectedResult=Create branch returned unexpected result {0}
-creatingDeltasIsNotImplemented=creating deltas is not implemented
+credentialPassword=Password
+credentialUsername=Username
 daemonAlreadyRunning=Daemon already running
 deleteBranchUnexpectedResult=Delete branch returned unexpected result {0}
 deletingNotSupported=Deleting {0} not supported.
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
index 6a75a9e..2eb316e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
@@ -184,7 +184,8 @@ public static JGitText get() {
 	/***/ public String countingObjects;
 	/***/ public String createBranchFailedUnknownReason;
 	/***/ public String createBranchUnexpectedResult;
-	/***/ public String creatingDeltasIsNotImplemented;
+	/***/ public String credentialPassword;
+	/***/ public String credentialUsername;
 	/***/ public String daemonAlreadyRunning;
 	/***/ public String deleteBranchUnexpectedResult;
 	/***/ public String deletingNotSupported;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
index e04b797..4d97e7c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheTree.java
@@ -44,9 +44,9 @@
 
 package org.eclipse.jgit.dircache;
 
-import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.FileMode.TREE;
+import static org.eclipse.jgit.lib.TreeFormatter.entrySize;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
@@ -55,9 +55,9 @@
 
 import org.eclipse.jgit.errors.UnmergedPathException;
 import org.eclipse.jgit.lib.Constants;
-import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.TreeFormatter;
 import org.eclipse.jgit.util.MutableInteger;
 import org.eclipse.jgit.util.RawParseUtils;
 
@@ -315,8 +315,8 @@ ObjectId writeTree(final DirCacheEntry[] cache, int cIdx,
 			throws UnmergedPathException, IOException {
 		if (id == null) {
 			final int endIdx = cIdx + entrySpan;
-			final int size = computeSize(cache, cIdx, pathOffset, ow);
-			final ByteArrayOutputStream out = new ByteArrayOutputStream(size);
+			final TreeFormatter fmt = new TreeFormatter(computeSize(cache,
+					cIdx, pathOffset, ow));
 			int childIdx = 0;
 			int entryIdx = cIdx;
 
@@ -326,27 +326,19 @@ ObjectId writeTree(final DirCacheEntry[] cache, int cIdx,
 				if (childIdx < childCnt) {
 					final DirCacheTree st = children[childIdx];
 					if (st.contains(ep, pathOffset, ep.length)) {
-						FileMode.TREE.copyTo(out);
-						out.write(' ');
-						out.write(st.encodedName);
-						out.write(0);
-						st.id.copyRawTo(out);
-
+						fmt.append(st.encodedName, TREE, st.id);
 						entryIdx += st.entrySpan;
 						childIdx++;
 						continue;
 					}
 				}
 
-				e.getFileMode().copyTo(out);
-				out.write(' ');
-				out.write(ep, pathOffset, ep.length - pathOffset);
-				out.write(0);
-				out.write(e.idBuffer(), e.idOffset(), OBJECT_ID_LENGTH);
+				fmt.append(ep, pathOffset, ep.length - pathOffset, e
+						.getFileMode(), e.idBuffer(), e.idOffset());
 				entryIdx++;
 			}
 
-			id = ow.insert(Constants.OBJ_TREE, out.toByteArray());
+			id = fmt.insert(ow);
 		}
 		return id;
 	}
@@ -371,9 +363,7 @@ private int computeSize(final DirCacheEntry[] cache, int cIdx,
 					final int stOffset = pathOffset + st.nameLength() + 1;
 					st.writeTree(cache, entryIdx, stOffset, ow);
 
-					size += FileMode.TREE.copyToLength();
-					size += st.nameLength();
-					size += OBJECT_ID_LENGTH + 2;
+					size += entrySize(TREE, st.nameLength());
 
 					entryIdx += st.entrySpan;
 					childIdx++;
@@ -381,10 +371,7 @@ private int computeSize(final DirCacheEntry[] cache, int cIdx,
 				}
 			}
 
-			final FileMode mode = e.getFileMode();
-			size += mode.copyToLength();
-			size += ep.length - pathOffset;
-			size += OBJECT_ID_LENGTH + 2;
+			size += entrySize(e.getFileMode(), ep.length - pathOffset);
 			entryIdx++;
 		}
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedCredentialItem.java b/org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedCredentialItem.java
new file mode 100644
index 0000000..eb09588
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/errors/UnsupportedCredentialItem.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>,
+ * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
+ * 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.errors;
+
+import org.eclipse.jgit.transport.CredentialItem;
+import org.eclipse.jgit.transport.CredentialsProvider;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * An exception thrown when a {@link CredentialItem} is requested from a
+ * {@link CredentialsProvider} which is not supported by this provider.
+ */
+public class UnsupportedCredentialItem extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * Constructs an UnsupportedCredentialItem with the specified detail message
+	 * prefixed with provided URI.
+	 *
+	 * @param uri
+	 *            URI used for transport
+	 * @param s
+	 *            message
+	 */
+	public UnsupportedCredentialItem(final URIish uri, final String s) {
+		super(uri.setPass(null) + ": " + s);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
index 7b30cec..61cc15d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AnyObjectId.java
@@ -97,15 +97,61 @@ public static boolean equals(final AnyObjectId firstObjectId,
 	int w5;
 
 	/**
-	 * For ObjectIdMap
+	 * Get the first 8 bits of the ObjectId.
 	 *
-	 * @return a discriminator usable for a fan-out style map
+	 * This is a faster version of {@code getByte(0)}.
+	 *
+	 * @return a discriminator usable for a fan-out style map. Returned values
+	 *         are unsigned and thus are in the range [0,255] rather than the
+	 *         signed byte range of [-128, 127].
 	 */
 	public final int getFirstByte() {
 		return w1 >>> 24;
 	}
 
 	/**
+	 * Get any byte from the ObjectId.
+	 *
+	 * Callers hard-coding {@code getByte(0)} should instead use the much faster
+	 * special case variant {@link #getFirstByte()}.
+	 *
+	 * @param index
+	 *            index of the byte to obtain from the raw form of the ObjectId.
+	 *            Must be in range [0, {@link Constants#OBJECT_ID_LENGTH}).
+	 * @return the value of the requested byte at {@code index}. Returned values
+	 *         are unsigned and thus are in the range [0,255] rather than the
+	 *         signed byte range of [-128, 127].
+	 * @throws ArrayIndexOutOfBoundsException
+	 *             {@code index} is less than 0, equal to
+	 *             {@link Constants#OBJECT_ID_LENGTH}, or greater than
+	 *             {@link Constants#OBJECT_ID_LENGTH}.
+	 */
+	public final int getByte(int index) {
+		int w;
+		switch (index >> 2) {
+		case 0:
+			w = w1;
+			break;
+		case 1:
+			w = w2;
+			break;
+		case 2:
+			w = w3;
+			break;
+		case 3:
+			w = w4;
+			break;
+		case 4:
+			w = w5;
+			break;
+		default:
+			throw new ArrayIndexOutOfBoundsException(index);
+		}
+
+		return (w >>> (8 * (3 - (index & 3)))) & 0xff;
+	}
+
+	/**
 	 * Compare this ObjectId to another and obtain a sort ordering.
 	 *
 	 * @param other
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java
index 43e4dd9..f295f5b 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/FileMode.java
@@ -223,6 +223,23 @@ public void copyTo(final OutputStream os) throws IOException {
 	}
 
 	/**
+	 * Copy this mode as a sequence of octal US-ASCII bytes.
+	 *
+	 * The mode is copied as a sequence of octal digits using the US-ASCII
+	 * character encoding. The sequence does not use a leading '0' prefix to
+	 * indicate octal notation. This method is suitable for generation of a mode
+	 * string within a GIT tree object.
+	 *
+	 * @param buf
+	 *            buffer to copy the mode to.
+	 * @param ptr
+	 *            position within {@code buf} for first digit.
+	 */
+	public void copyTo(byte[] buf, int ptr) {
+		System.arraycopy(octalBytes, 0, buf, ptr, octalBytes.length);
+	}
+
+	/**
 	 * @return the number of bytes written by {@link #copyTo(OutputStream)}.
 	 */
 	public int copyToLength() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java
index 26e83d4..7d7dfc2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/MutableObjectId.java
@@ -74,6 +74,60 @@ public MutableObjectId() {
 		fromObjectId(src);
 	}
 
+	/**
+	 * Set any byte in the id.
+	 *
+	 * @param index
+	 *            index of the byte to set in the raw form of the ObjectId. Must
+	 *            be in range [0, {@link Constants#OBJECT_ID_LENGTH}).
+	 * @param value
+	 *            the value of the specified byte at {@code index}. Values are
+	 *            unsigned and thus are in the range [0,255] rather than the
+	 *            signed byte range of [-128, 127].
+	 * @throws ArrayIndexOutOfBoundsException
+	 *             {@code index} is less than 0, equal to
+	 *             {@link Constants#OBJECT_ID_LENGTH}, or greater than
+	 *             {@link Constants#OBJECT_ID_LENGTH}.
+	 */
+	public void setByte(int index, int value) {
+		switch (index >> 2) {
+		case 0:
+			w1 = set(w1, index & 3, value);
+			break;
+		case 1:
+			w2 = set(w2, index & 3, value);
+			break;
+		case 2:
+			w3 = set(w3, index & 3, value);
+			break;
+		case 3:
+			w4 = set(w4, index & 3, value);
+			break;
+		case 4:
+			w5 = set(w5, index & 3, value);
+			break;
+		default:
+			throw new ArrayIndexOutOfBoundsException(index);
+		}
+	}
+
+	private static int set(int w, int index, int value) {
+		value &= 0xff;
+
+		switch (index) {
+		case 0:
+			return (w & 0x00ffffff) | (value << 24);
+		case 1:
+			return (w & 0xff00ffff) | (value << 16);
+		case 2:
+			return (w & 0xffff00ff) | (value << 8);
+		case 3:
+			return (w & 0xffffff00) | value;
+		default:
+			throw new ArrayIndexOutOfBoundsException();
+		}
+	}
+
 	/** Make this id match {@link ObjectId#zeroId()}. */
 	public void clear() {
 		w1 = 0;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
index 2eb578b..06ef429 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Tree.java
@@ -45,7 +45,6 @@
 
 package org.eclipse.jgit.lib;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.text.MessageFormat;
 
@@ -614,20 +613,16 @@ else if (FileMode.GITLINK.equals(mode))
 	 *             the tree cannot be loaded, or its not in a writable state.
 	 */
 	public byte[] format() throws IOException {
-		ByteArrayOutputStream o = new ByteArrayOutputStream();
+		TreeFormatter fmt = new TreeFormatter();
 		for (TreeEntry e : members()) {
 			ObjectId id = e.getId();
 			if (id == null)
 				throw new ObjectWritingException(MessageFormat.format(JGitText
 						.get().objectAtPathDoesNotHaveId, e.getFullName()));
 
-			e.getMode().copyTo(o);
-			o.write(' ');
-			o.write(e.getNameUTF8());
-			o.write(0);
-			id.copyRawTo(o);
+			fmt.append(e.getNameUTF8(), e.getMode(), id);
 		}
-		return o.toByteArray();
+		return fmt.toByteArray();
 	}
 
 	public String toString() {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeFormatter.java
new file mode 100644
index 0000000..e14e81f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/TreeFormatter.java
@@ -0,0 +1,333 @@
+/*
+ * 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.lib;
+
+import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
+import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
+import static org.eclipse.jgit.lib.Constants.encode;
+import static org.eclipse.jgit.lib.FileMode.GITLINK;
+import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
+import static org.eclipse.jgit.lib.FileMode.TREE;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.revwalk.RevBlob;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+/**
+ * Mutable formatter to construct a single tree object.
+ *
+ * This formatter does not process subtrees. Callers must handle creating each
+ * subtree on their own.
+ *
+ * To maintain good performance for bulk operations, this formatter does not
+ * validate its input. Callers are responsible for ensuring the resulting tree
+ * object is correctly well formed by writing entries in the correct order.
+ */
+public class TreeFormatter {
+	/**
+	 * Compute the size of a tree entry record.
+	 *
+	 * This method can be used to estimate the correct size of a tree prior to
+	 * allocating a formatter. Getting the size correct at allocation time
+	 * ensures the internal buffer is sized correctly, reducing copying.
+	 *
+	 * @param mode
+	 *            the mode the entry will have.
+	 * @param nameLen
+	 *            the length of the name, in bytes.
+	 * @return the length of the record.
+	 */
+	public static int entrySize(FileMode mode, int nameLen) {
+		return mode.copyToLength() + nameLen + OBJECT_ID_LENGTH + 2;
+	}
+
+	private byte[] buf;
+
+	private int ptr;
+
+	private TemporaryBuffer.Heap overflowBuffer;
+
+	/** Create an empty formatter with a default buffer size. */
+	public TreeFormatter() {
+		this(8192);
+	}
+
+	/**
+	 * Create an empty formatter with the specified buffer size.
+	 *
+	 * @param size
+	 *            estimated size of the tree, in bytes. Callers can use
+	 *            {@link #entrySize(FileMode, int)} to estimate the size of each
+	 *            entry in advance of allocating the formatter.
+	 */
+	public TreeFormatter(int size) {
+		buf = new byte[size];
+	}
+
+	/**
+	 * Add a link to a submodule commit, mode is {@link #GITLINK}.
+	 *
+	 * @param name
+	 *            name of the entry.
+	 * @param commit
+	 *            the ObjectId to store in this entry.
+	 */
+	public void append(String name, RevCommit commit) {
+		append(name, GITLINK, commit);
+	}
+
+	/**
+	 * Add a subtree, mode is {@link #TREE}.
+	 *
+	 * @param name
+	 *            name of the entry.
+	 * @param tree
+	 *            the ObjectId to store in this entry.
+	 */
+	public void append(String name, RevTree tree) {
+		append(name, TREE, tree);
+	}
+
+	/**
+	 * Add a regular file, mode is {@link #REGULAR_FILE}.
+	 *
+	 * @param name
+	 *            name of the entry.
+	 * @param blob
+	 *            the ObjectId to store in this entry.
+	 */
+	public void append(String name, RevBlob blob) {
+		append(name, REGULAR_FILE, blob);
+	}
+
+	/**
+	 * Append any entry to the tree.
+	 *
+	 * @param name
+	 *            name of the entry.
+	 * @param mode
+	 *            mode describing the treatment of {@code id}.
+	 * @param id
+	 *            the ObjectId to store in this entry.
+	 */
+	public void append(String name, FileMode mode, AnyObjectId id) {
+		append(encode(name), mode, id);
+	}
+
+	/**
+	 * Append any entry to the tree.
+	 *
+	 * @param name
+	 *            name of the entry. The name should be UTF-8 encoded, but file
+	 *            name encoding is not a well defined concept in Git.
+	 * @param mode
+	 *            mode describing the treatment of {@code id}.
+	 * @param id
+	 *            the ObjectId to store in this entry.
+	 */
+	public void append(byte[] name, FileMode mode, AnyObjectId id) {
+		append(name, 0, name.length, mode, id);
+	}
+
+	/**
+	 * Append any entry to the tree.
+	 *
+	 * @param nameBuf
+	 *            buffer holding the name of the entry. The name should be UTF-8
+	 *            encoded, but file name encoding is not a well defined concept
+	 *            in Git.
+	 * @param namePos
+	 *            first position within {@code nameBuf} of the name data.
+	 * @param nameLen
+	 *            number of bytes from {@code nameBuf} to use as the name.
+	 * @param mode
+	 *            mode describing the treatment of {@code id}.
+	 * @param id
+	 *            the ObjectId to store in this entry.
+	 */
+	public void append(byte[] nameBuf, int namePos, int nameLen, FileMode mode,
+			AnyObjectId id) {
+		if (fmtBuf(nameBuf, namePos, nameLen, mode)) {
+			id.copyRawTo(buf, ptr);
+			ptr += OBJECT_ID_LENGTH;
+
+		} else {
+			try {
+				fmtOverflowBuffer(nameBuf, namePos, nameLen, mode);
+				id.copyRawTo(overflowBuffer);
+			} catch (IOException badBuffer) {
+				// This should never occur.
+				throw new RuntimeException(badBuffer);
+			}
+		}
+	}
+
+	/**
+	 * Append any entry to the tree.
+	 *
+	 * @param nameBuf
+	 *            buffer holding the name of the entry. The name should be UTF-8
+	 *            encoded, but file name encoding is not a well defined concept
+	 *            in Git.
+	 * @param namePos
+	 *            first position within {@code nameBuf} of the name data.
+	 * @param nameLen
+	 *            number of bytes from {@code nameBuf} to use as the name.
+	 * @param mode
+	 *            mode describing the treatment of {@code id}.
+	 * @param idBuf
+	 *            buffer holding the raw ObjectId of the entry.
+	 * @param idPos
+	 *            first position within {@code idBuf} to copy the id from.
+	 */
+	public void append(byte[] nameBuf, int namePos, int nameLen, FileMode mode,
+			byte[] idBuf, int idPos) {
+		if (fmtBuf(nameBuf, namePos, nameLen, mode)) {
+			System.arraycopy(idBuf, idPos, buf, ptr, OBJECT_ID_LENGTH);
+			ptr += OBJECT_ID_LENGTH;
+
+		} else {
+			try {
+				fmtOverflowBuffer(nameBuf, namePos, nameLen, mode);
+				overflowBuffer.write(idBuf, idPos, OBJECT_ID_LENGTH);
+			} catch (IOException badBuffer) {
+				// This should never occur.
+				throw new RuntimeException(badBuffer);
+			}
+		}
+	}
+
+	private boolean fmtBuf(byte[] nameBuf, int namePos, int nameLen,
+			FileMode mode) {
+		if (buf == null || buf.length < ptr + entrySize(mode, nameLen))
+			return false;
+
+		mode.copyTo(buf, ptr);
+		ptr += mode.copyToLength();
+		buf[ptr++] = ' ';
+
+		System.arraycopy(nameBuf, namePos, buf, ptr, nameLen);
+		ptr += nameLen;
+		buf[ptr++] = 0;
+		return true;
+	}
+
+	private void fmtOverflowBuffer(byte[] nameBuf, int namePos, int nameLen,
+			FileMode mode) throws IOException {
+		if (buf != null) {
+			overflowBuffer = new TemporaryBuffer.Heap(Integer.MAX_VALUE);
+			overflowBuffer.write(buf, 0, ptr);
+			buf = null;
+		}
+
+		mode.copyTo(overflowBuffer);
+		overflowBuffer.write((byte) ' ');
+		overflowBuffer.write(nameBuf, namePos, nameLen);
+		overflowBuffer.write((byte) 0);
+	}
+
+	/**
+	 * Compute the current tree's ObjectId.
+	 *
+	 * @return computed ObjectId of the tree
+	 */
+	public ObjectId getTreeId() {
+		final ObjectInserter.Formatter fmt = new ObjectInserter.Formatter();
+		if (buf != null)
+			return fmt.idFor(OBJ_TREE, buf, 0, ptr);
+
+		try {
+			final long len = overflowBuffer.length();
+			return fmt.idFor(OBJ_TREE, len, overflowBuffer.openInputStream());
+		} catch (IOException err) {
+			// This should never happen, its read failure on a byte array.
+			throw new RuntimeException(err);
+		}
+	}
+
+	/**
+	 * Insert this tree and obtain its ObjectId.
+	 *
+	 * @param ins
+	 *            the inserter to store the tree.
+	 * @return computed ObjectId of the tree
+	 * @throws IOException
+	 *             the tree could not be stored.
+	 */
+	public ObjectId insert(ObjectInserter ins) throws IOException {
+		if (buf != null)
+			return ins.insert(OBJ_TREE, buf, 0, ptr);
+
+		final long len = overflowBuffer.length();
+		return ins.insert(OBJ_TREE, len, overflowBuffer.openInputStream());
+	}
+
+	/**
+	 * Copy this formatter's buffer into a byte array.
+	 *
+	 * This method is not efficient, as it needs to create a copy of the
+	 * internal buffer in order to supply an array of the correct size to the
+	 * caller. If the buffer is just to pass to an ObjectInserter, consider
+	 * using {@link #insert(ObjectInserter)} instead.
+	 *
+	 * @return a copy of this formatter's buffer.
+	 */
+	public byte[] toByteArray() {
+		if (buf != null) {
+			byte[] r = new byte[ptr];
+			System.arraycopy(buf, 0, r, 0, ptr);
+			return r;
+		}
+
+		try {
+			return overflowBuffer.toByteArray();
+		} catch (IOException err) {
+			// This should never happen, its read failure on a byte array.
+			throw new RuntimeException(err);
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
new file mode 100644
index 0000000..85337c8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/FanoutBucket.java
@@ -0,0 +1,126 @@
+/*
+ * 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.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+
+/**
+ * A note tree holding only note subtrees, each named using a 2 digit hex name.
+ *
+ * The fanout buckets/trees contain on average 256 subtrees, naming the subtrees
+ * by a slice of the ObjectId contained within them, from "00" through "ff".
+ *
+ * Each fanout bucket has a {@link #prefixLen} that defines how many digits it
+ * skips in an ObjectId before it gets to the digits matching {@link #table}.
+ *
+ * The root tree has {@code prefixLen == 0}, and thus does not skip any digits.
+ * For ObjectId "c0ffee...", the note (if it exists) will be stored within the
+ * bucket {@code table[0xc0]}.
+ *
+ * The first level tree has {@code prefixLen == 2}, and thus skips the first two
+ * digits. For the same example "c0ffee..." object, its note would be found
+ * within the {@code table[0xff]} bucket (as first 2 digits "c0" are skipped).
+ *
+ * Each subtree is loaded on-demand, reducing startup latency for reads that
+ * only need to examine a few objects. However, due to the rather uniform
+ * distribution of the SHA-1 hash that is used for ObjectIds, accessing 256
+ * objects is very likely to load all of the subtrees into memory.
+ *
+ * A FanoutBucket must be parsed from a tree object by {@link NoteParser}.
+ */
+class FanoutBucket extends InMemoryNoteBucket {
+	/**
+	 * Fan-out table similar to the PackIndex structure.
+	 *
+	 * Notes for an object are stored within the sub-bucket that is held here as
+	 * {@code table[ objectId.getByte( prefixLen / 2 ) ]}. If the slot is null
+	 * there are no notes with that prefix.
+	 */
+	private final NoteBucket[] table;
+
+	FanoutBucket(int prefixLen) {
+		super(prefixLen);
+		table = new NoteBucket[256];
+	}
+
+	void parseOneEntry(int cell, ObjectId id) {
+		table[cell] = new LazyNoteBucket(id);
+	}
+
+	@Override
+	ObjectId get(AnyObjectId objId, ObjectReader or) throws IOException {
+		NoteBucket b = table[cell(objId)];
+		return b != null ? b.get(objId, or) : null;
+	}
+
+	private int cell(AnyObjectId id) {
+		return id.getByte(prefixLen >> 1);
+	}
+
+	private class LazyNoteBucket extends NoteBucket {
+		private final ObjectId treeId;
+
+		LazyNoteBucket(ObjectId treeId) {
+			this.treeId = treeId;
+		}
+
+		@Override
+		ObjectId get(AnyObjectId objId, ObjectReader or) throws IOException {
+			return load(objId, or).get(objId, or);
+		}
+
+		private NoteBucket load(AnyObjectId objId, ObjectReader or)
+				throws IOException {
+			AbbreviatedObjectId p = objId.abbreviate(prefixLen + 2);
+			NoteBucket self = NoteParser.parse(p, treeId, or);
+			table[cell(objId)] = self;
+			return self;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/InMemoryNoteBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/InMemoryNoteBucket.java
new file mode 100644
index 0000000..7d0df73
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/InMemoryNoteBucket.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+/** A note bucket that has been loaded into the process. */
+abstract class InMemoryNoteBucket extends NoteBucket {
+	/**
+	 * Number of leading digits that leads to this bucket in the note path.
+	 *
+	 * This is counted in terms of hex digits, not raw bytes. Each bucket level
+	 * is typically 2 higher than its parent, placing about 256 items in each
+	 * level of the tree.
+	 */
+	final int prefixLen;
+
+	InMemoryNoteBucket(int prefixLen) {
+		this.prefixLen = prefixLen;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
new file mode 100644
index 0000000..66d773a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/LeafBucket.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+import org.eclipse.jgit.lib.ObjectReader;
+
+/**
+ * A note tree holding only notes, with no subtrees.
+ *
+ * The leaf bucket contains on average less than 256 notes, all of whom share
+ * the same leading prefix. If a notes branch has less than 256 notes, the top
+ * level tree of the branch should be a LeafBucket. Once a notes branch has more
+ * than 256 notes, the root should be a {@link FanoutBucket} and the LeafBucket
+ * will appear only as a cell of a FanoutBucket.
+ *
+ * Entries within the LeafBucket are stored sorted by ObjectId, and lookup is
+ * performed using binary search. As the entry list should contain fewer than
+ * 256 elements, the average number of compares to find an element should be
+ * less than 8 due to the O(log N) lookup behavior.
+ *
+ * A LeafBucket must be parsed from a tree object by {@link NoteParser}.
+ */
+class LeafBucket extends InMemoryNoteBucket {
+	/** All note blobs in this bucket, sorted sequentially. */
+	private Note[] notes;
+
+	/** Number of items in {@link #notes}. */
+	private int cnt;
+
+	LeafBucket(int prefixLen) {
+		super(prefixLen);
+		notes = new Note[4];
+	}
+
+	private int search(AnyObjectId objId) {
+		int low = 0;
+		int high = cnt;
+		while (low < high) {
+			int mid = (low + high) >>> 1;
+			int cmp = objId.compareTo(notes[mid]);
+			if (cmp < 0)
+				high = mid;
+			else if (cmp == 0)
+				return mid;
+			else
+				low = mid + 1;
+		}
+		return -(low + 1);
+	}
+
+	ObjectId get(AnyObjectId objId, ObjectReader or) {
+		int idx = search(objId);
+		return 0 <= idx ? notes[idx].getData() : null;
+	}
+
+	void parseOneEntry(AnyObjectId noteOn, AnyObjectId noteData) {
+		growIfFull();
+		notes[cnt++] = new Note(noteOn, noteData.copy());
+	}
+
+	private void growIfFull() {
+		if (notes.length == cnt) {
+			Note[] n = new Note[notes.length * 2];
+			System.arraycopy(notes, 0, n, 0, cnt);
+			notes = n;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/Note.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/Note.java
new file mode 100644
index 0000000..d365f9b
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/Note.java
@@ -0,0 +1,78 @@
+/*
+ * 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() + "]";
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteBucket.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteBucket.java
new file mode 100644
index 0000000..286f140
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteBucket.java
@@ -0,0 +1,61 @@
+/*
+ * 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.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+
+/**
+ * A tree that stores note objects.
+ *
+ * @see FanoutBucket
+ * @see LeafBucket
+ */
+abstract class NoteBucket {
+	abstract ObjectId get(AnyObjectId objId, ObjectReader reader)
+			throws IOException;
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
new file mode 100644
index 0000000..d2f0727
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteMap.java
@@ -0,0 +1,221 @@
+/*
+ * 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.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+
+/**
+ * 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 reader can be used during any call, and
+ * is not released by this class. The caller should arrange for releasing the
+ * shared {@code ObjectReader} at the proper times.
+ */
+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 InMemoryNoteBucket root;
+
+	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.
+	 * @throws IOException
+	 *             a portion of the note space is not accessible.
+	 */
+	public ObjectId get(AnyObjectId id) throws IOException {
+		return root.get(id, reader);
+	}
+
+	/**
+	 * 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.
+	 * @throws IOException
+	 *             a portion of the note space is not accessible.
+	 */
+	public boolean contains(AnyObjectId id) throws IOException {
+		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 rootTree) throws MissingObjectException,
+			IncorrectObjectTypeException, CorruptObjectException, IOException {
+		AbbreviatedObjectId none = AbbreviatedObjectId.fromString("");
+		root = NoteParser.parse(none, rootTree, reader);
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteParser.java b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteParser.java
new file mode 100644
index 0000000..04e260a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/notes/NoteParser.java
@@ -0,0 +1,184 @@
+/*
+ * 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 static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
+import static org.eclipse.jgit.lib.Constants.encodeASCII;
+import static org.eclipse.jgit.lib.FileMode.TREE;
+import static org.eclipse.jgit.util.RawParseUtils.parseHexInt4;
+
+import java.io.IOException;
+
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.lib.AbbreviatedObjectId;
+import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+
+/** Custom tree parser to select note bucket type and load it. */
+final class NoteParser extends CanonicalTreeParser {
+	/**
+	 * Parse a tree object into a {@link NoteBucket} instance.
+	 *
+	 * The type of note tree is automatically detected by examining the items
+	 * within the tree, and allocating the proper storage type based on the
+	 * first note-like entry encountered. Since the method parses by guessing
+	 * the type on the first element, malformed note trees can be read as the
+	 * wrong type of tree.
+	 *
+	 * This method is not recursive, it parses the one tree given to it and
+	 * returns the bucket. If there are subtrees for note storage, they are
+	 * setup as lazy pointers that will be resolved at a later time.
+	 *
+	 * @param prefix
+	 *            common hex digits that all notes within this tree share. The
+	 *            root tree has {@code prefix.length() == 0}, the first-level
+	 *            subtrees should be {@code prefix.length()==2}, etc.
+	 * @param treeId
+	 *            the tree to read from the repository.
+	 * @param reader
+	 *            reader to access the tree object.
+	 * @return bucket to holding the notes of the specified tree.
+	 * @throws IOException
+	 *             {@code treeId} cannot be accessed.
+	 */
+	static InMemoryNoteBucket parse(AbbreviatedObjectId prefix,
+			final ObjectId treeId, final ObjectReader reader)
+			throws IOException {
+		return new NoteParser(prefix, reader, treeId).parseTree();
+	}
+
+	private final AbbreviatedObjectId prefix;
+
+	private final int pathPadding;
+
+	private NoteParser(AbbreviatedObjectId p, ObjectReader r, ObjectId t)
+			throws IncorrectObjectTypeException, IOException {
+		super(encodeASCII(p.name()), r, t);
+		prefix = p;
+
+		// Our path buffer has a '/' that we don't want after the prefix.
+		// Drop it by shifting the path down one position.
+		pathPadding = 0 < prefix.length() ? 1 : 0;
+		if (0 < pathPadding)
+			System.arraycopy(path, 0, path, pathPadding, prefix.length());
+	}
+
+	private InMemoryNoteBucket parseTree() {
+		for (; !eof(); next(1)) {
+			if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH && isHex())
+				return parseLeafTree();
+
+			else if (getNameLength() == 2 && isHex() && isTree())
+				return parseFanoutTree();
+		}
+
+		// If we cannot determine the style used, assume its a leaf.
+		return new LeafBucket(prefix.length());
+	}
+
+	private LeafBucket parseLeafTree() {
+		final LeafBucket leaf = new LeafBucket(prefix.length());
+		final MutableObjectId idBuf = new MutableObjectId();
+
+		for (; !eof(); next(1)) {
+			if (parseObjectId(idBuf))
+				leaf.parseOneEntry(idBuf, getEntryObjectId());
+		}
+
+		return leaf;
+	}
+
+	private boolean parseObjectId(MutableObjectId id) {
+		if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH) {
+			try {
+				id.fromString(path, pathPadding);
+				return true;
+			} catch (ArrayIndexOutOfBoundsException notHex) {
+				return false;
+			}
+		}
+		return false;
+	}
+
+	private FanoutBucket parseFanoutTree() {
+		final FanoutBucket fanout = new FanoutBucket(prefix.length());
+
+		for (; !eof(); next(1)) {
+			final int cell = parseFanoutCell();
+			if (0 <= cell)
+				fanout.parseOneEntry(cell, getEntryObjectId());
+		}
+
+		return fanout;
+	}
+
+	private int parseFanoutCell() {
+		if (getNameLength() == 2 && isTree()) {
+			try {
+				return (parseHexInt4(path[pathOffset + 0]) << 4)
+						| parseHexInt4(path[pathOffset + 1]);
+			} catch (ArrayIndexOutOfBoundsException notHex) {
+				return -1;
+			}
+		} else {
+			return -1;
+		}
+	}
+
+	private boolean isTree() {
+		return TREE.equals(mode);
+	}
+
+	private boolean isHex() {
+		try {
+			for (int i = pathOffset; i < pathLen; i++)
+				parseHexInt4(path[i]);
+			return true;
+		} catch (ArrayIndexOutOfBoundsException fail) {
+			return false;
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialItem.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialItem.java
new file mode 100644
index 0000000..55ce4db
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialItem.java
@@ -0,0 +1,283 @@
+/*
+ * 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.transport;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.JGitText;
+
+/**
+ * A credential requested from a {@link CredentialsProvider}.
+ *
+ * Most users should work with the specialized subclasses:
+ * <ul>
+ * <li>{@link Username} for usernames</li>
+ * <li>{@link Password} for passwords</li>
+ * <li>{@link StringType} for other general string information</li>
+ * <li>{@link CharArrayType} for other general secret information</li>
+ * </ul>
+ *
+ * This class is not thread-safe. Applications should construct their own
+ * instance for each use, as the value is held within the CredentialItem object.
+ */
+public abstract class CredentialItem {
+	private final String promptText;
+
+	private final boolean valueSecure;
+
+	/**
+	 * Initialize a prompt.
+	 *
+	 * @param promptText
+	 *            prompt to display to the user alongside of the input field.
+	 *            Should be sufficient text to indicate what to supply for this
+	 *            item.
+	 * @param maskValue
+	 *            true if the value should be masked from displaying during
+	 *            input. This should be true for passwords and other secrets,
+	 *            false for names and other public data.
+	 */
+	public CredentialItem(String promptText, boolean maskValue) {
+		this.promptText = promptText;
+		this.valueSecure = maskValue;
+	}
+
+	/** @return prompt to display to the user. */
+	public String getPromptText() {
+		return promptText;
+	}
+
+	/** @return true if the value should be masked when entered. */
+	public boolean isValueSecure() {
+		return valueSecure;
+	}
+
+	/** Clear the stored value, destroying it as much as possible. */
+	public abstract void clear();
+
+	/**
+	 * An item whose value is stored as a string.
+	 *
+	 * When working with secret data, consider {@link CharArrayType} instead, as
+	 * the internal members of the array can be cleared, reducing the chances
+	 * that the password is left in memory after authentication is completed.
+	 */
+	public static class StringType extends CredentialItem {
+		private String value;
+
+		/**
+		 * Initialize a prompt for a single string.
+		 *
+		 * @param promptText
+		 *            prompt to display to the user alongside of the input
+		 *            field. Should be sufficient text to indicate what to
+		 *            supply for this item.
+		 * @param maskValue
+		 *            true if the value should be masked from displaying during
+		 *            input. This should be true for passwords and other
+		 *            secrets, false for names and other public data.
+		 */
+		public StringType(String promptText, boolean maskValue) {
+			super(promptText, maskValue);
+		}
+
+		@Override
+		public void clear() {
+			value = null;
+		}
+
+		/** @return the current value */
+		public String getValue() {
+			return value;
+		}
+
+		/**
+		 *
+		 * @param newValue
+		 */
+		public void setValue(String newValue) {
+			value = newValue;
+		}
+	}
+
+	/** An item whose value is stored as a char[] and is therefore clearable. */
+	public static class CharArrayType extends CredentialItem {
+		private char[] value;
+
+		/**
+		 * Initialize a prompt for a secure value stored in a character array.
+		 *
+		 * @param promptText
+		 *            prompt to display to the user alongside of the input
+		 *            field. Should be sufficient text to indicate what to
+		 *            supply for this item.
+		 * @param maskValue
+		 *            true if the value should be masked from displaying during
+		 *            input. This should be true for passwords and other
+		 *            secrets, false for names and other public data.
+		 */
+		public CharArrayType(String promptText, boolean maskValue) {
+			super(promptText, maskValue);
+		}
+
+		/** Destroys the current value, clearing the internal array. */
+		@Override
+		public void clear() {
+			if (value != null) {
+				Arrays.fill(value, (char) 0);
+				value = null;
+			}
+		}
+
+		/**
+		 * Get the current value.
+		 *
+		 * The returned array will be cleared out when {@link #clear()} is
+		 * called. Callers that need the array elements to survive should delay
+		 * invoking {@code clear()} until the value is no longer necessary.
+		 *
+		 * @return the current value array. The actual internal array is
+		 *         returned, reducing the number of copies present in memory.
+		 */
+		public char[] getValue() {
+			return value;
+		}
+
+		/**
+		 * Set the new value, clearing the old value array.
+		 *
+		 * @param newValue
+		 *            if not null, the array is copied.
+		 */
+		public void setValue(char[] newValue) {
+			clear();
+
+			if (newValue != null) {
+				value = new char[newValue.length];
+				System.arraycopy(newValue, 0, value, 0, newValue.length);
+			}
+		}
+
+		/**
+		 * Set the new value, clearing the old value array.
+		 *
+		 * @param newValue
+		 *            the new internal array. The array is <b>NOT</b> copied.
+		 */
+		public void setValueNoCopy(char[] newValue) {
+			clear();
+			value = newValue;
+		}
+	}
+
+	/** An item whose value is a boolean choice, presented as Yes/No. */
+	public static class YesNoType extends CredentialItem {
+		private boolean value;
+
+		/**
+		 * Initialize a prompt for a single boolean answer.
+		 *
+		 * @param promptText
+		 *            prompt to display to the user alongside of the input
+		 *            field. Should be sufficient text to indicate what to
+		 *            supply for this item.
+		 */
+		public YesNoType(String promptText) {
+			super(promptText, false);
+		}
+
+		@Override
+		public void clear() {
+			value = false;
+		}
+
+		/** @return the current value */
+		public boolean getValue() {
+			return value;
+		}
+
+		/**
+		 * Set the new value.
+		 *
+		 * @param newValue
+		 */
+		public void setValue(boolean newValue) {
+			value = newValue;
+		}
+	}
+
+	/** An advice message presented to the user, with no response required. */
+	public static class InformationalMessage extends CredentialItem {
+		/**
+		 * Initialize an informational message.
+		 *
+		 * @param messageText
+		 *            message to display to the user.
+		 */
+		public InformationalMessage(String messageText) {
+			super(messageText, false);
+		}
+
+		@Override
+		public void clear() {
+			// Nothing to clear.
+		}
+	}
+
+	/** Prompt for a username, which is not masked on input. */
+	public static class Username extends StringType {
+		/** Initialize a new username item, with a default username prompt. */
+		public Username() {
+			super(JGitText.get().credentialUsername, false);
+		}
+	}
+
+	/** Prompt for a password, which is masked on input. */
+	public static class Password extends CharArrayType {
+		/** Initialize a new password item, with a default password prompt. */
+		public Password() {
+			super(JGitText.get().credentialPassword, true);
+		}
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProvider.java
new file mode 100644
index 0000000..194268f
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProvider.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>,
+ * Copyright (C) 2010, Stefan Lay <stefan.lay@sap.com>
+ * 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.transport;
+
+import java.util.List;
+
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+
+/**
+ * Provide credentials for use in connecting to Git repositories.
+ *
+ * Implementors are strongly encouraged to support at least the minimal
+ * {@link CredentialItem.Username} and {@link CredentialItem.Password} items.
+ * More sophisticated implementors may implement additional types, such as
+ * {@link CredentialItem.StringType}.
+ *
+ * CredentialItems are usually presented in bulk, allowing implementors to
+ * combine them into a single UI widget and streamline the authentication
+ * process for an end-user.
+ *
+ * @see UsernamePasswordCredentialsProvider
+ */
+public abstract class CredentialsProvider {
+	private static volatile CredentialsProvider defaultProvider;
+
+	/** @return the default credentials provider, or null. */
+	public static CredentialsProvider getDefault() {
+		return defaultProvider;
+	}
+
+	/**
+	 * Set the default credentials provider.
+	 *
+	 * @param p
+	 *            the new default provider, may be null to select no default.
+	 */
+	public static void setDefault(CredentialsProvider p) {
+		defaultProvider = p;
+	}
+
+	/**
+	 * Check if the provider is interactive with the end-user.
+	 *
+	 * An interactive provider may try to open a dialog box, or prompt for input
+	 * on the terminal, and will wait for a user response. A non-interactive
+	 * provider will either populate CredentialItems, or fail.
+	 *
+	 * @return {@code true} if the provider is interactive with the end-user.
+	 */
+	public abstract boolean isInteractive();
+
+	/**
+	 * Check if the provider can supply the necessary {@link CredentialItem}s.
+	 *
+	 * @param items
+	 *            the items the application requires to complete authentication.
+	 * @return {@code true} if this {@link CredentialsProvider} supports all of
+	 *         the items supplied.
+	 */
+	public abstract boolean supports(CredentialItem... items);
+
+	/**
+	 * Ask for the credential items to be populated.
+	 *
+	 * @param uri
+	 *            the URI of the remote resource that needs authentication.
+	 * @param items
+	 *            the items the application requires to complete authentication.
+	 * @return {@code true} if the request was successful and values were
+	 *         supplied; {@code false} if the user canceled the request and did
+	 *         not supply all requested values.
+	 * @throws UnsupportedCredentialItem
+	 *             if one of the items supplied is not supported.
+	 */
+	public abstract boolean get(URIish uri, CredentialItem... items)
+			throws UnsupportedCredentialItem;
+
+	/**
+	 * Ask for the credential items to be populated.
+	 *
+	 * @param uri
+	 *            the URI of the remote resource that needs authentication.
+	 * @param items
+	 *            the items the application requires to complete authentication.
+	 * @return {@code true} if the request was successful and values were
+	 *         supplied; {@code false} if the user canceled the request and did
+	 *         not supply all requested values.
+	 * @throws UnsupportedCredentialItem
+	 *             if one of the items supplied is not supported.
+	 */
+	public boolean get(URIish uri, List<CredentialItem> items)
+			throws UnsupportedCredentialItem {
+		return get(uri, items.toArray(new CredentialItem[items.size()]));
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
new file mode 100644
index 0000000..8f259c6
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/CredentialsProviderUserInfo.java
@@ -0,0 +1,150 @@
+/*
+ * 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.transport;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.UIKeyboardInteractive;
+import com.jcraft.jsch.UserInfo;
+
+/** A JSch {@link UserInfo} adapter for a {@link CredentialsProvider}. */
+public class CredentialsProviderUserInfo implements UserInfo,
+		UIKeyboardInteractive {
+	private final URIish uri;
+
+	private final CredentialsProvider provider;
+
+	private String password;
+
+	private String passphrase;
+
+	/**
+	 * Wrap a CredentialsProvider to make it suitable for use with JSch.
+	 *
+	 * @param session
+	 *            the JSch session this UserInfo will support authentication on.
+	 * @param credentialsProvider
+	 *            the provider that will perform the authentication.
+	 */
+	public CredentialsProviderUserInfo(Session session,
+			CredentialsProvider credentialsProvider) {
+		this.uri = createURI(session);
+		this.provider = credentialsProvider;
+	}
+
+	private static URIish createURI(Session session) {
+		URIish uri = new URIish();
+		uri = uri.setScheme("ssh");
+		uri = uri.setUser(session.getUserName());
+		uri = uri.setHost(session.getHost());
+		uri = uri.setPort(session.getPort());
+		return uri;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public String getPassphrase() {
+		return passphrase;
+	}
+
+	public boolean promptPassphrase(String msg) {
+		CredentialItem.StringType v = newPrompt(msg);
+		if (provider.get(uri, v)) {
+			passphrase = v.getValue();
+			return true;
+		} else {
+			passphrase = null;
+			return false;
+		}
+	}
+
+	public boolean promptPassword(String msg) {
+		CredentialItem.StringType v = newPrompt(msg);
+		if (provider.get(uri, v)) {
+			password = v.getValue();
+			return true;
+		} else {
+			password = null;
+			return false;
+		}
+	}
+
+	private CredentialItem.StringType newPrompt(String msg) {
+		return new CredentialItem.StringType(msg, true);
+	}
+
+	public boolean promptYesNo(String msg) {
+		CredentialItem.YesNoType v = new CredentialItem.YesNoType(msg);
+		return provider.get(uri, v) && v.getValue();
+	}
+
+	public void showMessage(String msg) {
+		provider.get(uri, new CredentialItem.InformationalMessage(msg));
+	}
+
+	public String[] promptKeyboardInteractive(String destination, String name,
+			String instruction, String[] prompt, boolean[] echo) {
+		CredentialItem.StringType[] v = new CredentialItem.StringType[prompt.length];
+		for (int i = 0; i < prompt.length; i++)
+			v[i] = new CredentialItem.StringType(prompt[i], !echo[i]);
+
+		List<CredentialItem> items = new ArrayList<CredentialItem>();
+		if (instruction != null && instruction.length() > 0)
+			items.add(new CredentialItem.InformationalMessage(instruction));
+		items.addAll(Arrays.asList(v));
+
+		if (!provider.get(uri, items))
+			return null; // cancel
+
+		String[] result = new String[v.length];
+		for (int i = 0; i < v.length; i++)
+			result[i] = v[i].getValue();
+		return result;
+	}
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
index 5a559ab..9266616 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/HttpAuthMethod.java
@@ -85,9 +85,9 @@ static HttpAuthMethod scanResponse(HttpURLConnection conn) {
 			return NONE;
 
 		String type = hdr.substring(0, sp);
-		if (Basic.NAME.equals(type))
+		if (Basic.NAME.equalsIgnoreCase(type))
 			return new Basic();
-		else if (Digest.NAME.equals(type))
+		else if (Digest.NAME.equalsIgnoreCase(type))
 			return new Digest(hdr.substring(sp + 1));
 		else
 			return NONE;
@@ -98,9 +98,37 @@ else if (Digest.NAME.equals(type))
 	 *
 	 * @param uri
 	 *            the URI used to create the connection.
+	 * @param credentialsProvider
+	 *            the credentials provider, or null. If provided,
+	 *            {@link URIish#getPass() credentials in the URI} are ignored.
+	 *
+	 * @return true if the authentication method is able to provide
+	 *         authorization for the given URI
 	 */
-	void authorize(URIish uri) {
-		authorize(uri.getUser(), uri.getPass());
+	boolean authorize(URIish uri, CredentialsProvider credentialsProvider) {
+		String username;
+		String password;
+
+		if (credentialsProvider != null) {
+			CredentialItem.Username u = new CredentialItem.Username();
+			CredentialItem.Password p = new CredentialItem.Password();
+
+			if (credentialsProvider.supports(u, p)
+					&& credentialsProvider.get(uri, u, p)) {
+				username = u.getValue();
+				password = new String(p.getValue());
+				p.clear();
+			} else
+				return false;
+		} else {
+			username = uri.getUser();
+			password = uri.getPass();
+		}
+		if (username != null) {
+			authorize(username, password);
+			return true;
+		}
+		return false;
 	}
 
 	/**
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java
index daa6f4c..99e7b83 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshConfigSessionFactory.java
@@ -83,7 +83,8 @@ public abstract class SshConfigSessionFactory extends SshSessionFactory {
 
 	@Override
 	public synchronized Session getSession(String user, String pass,
-			String host, int port, FS fs) throws JSchException {
+			String host, int port, CredentialsProvider credentialsProvider,
+			FS fs) throws JSchException {
 		if (config == null)
 			config = OpenSshConfig.get(fs);
 
@@ -105,6 +106,11 @@ public synchronized Session getSession(String user, String pass,
 		final String pauth = hc.getPreferredAuthentications();
 		if (pauth != null)
 			session.setConfig("PreferredAuthentications", pauth);
+		if (credentialsProvider != null
+				&& (!hc.isBatchMode() || !credentialsProvider.isInteractive())) {
+			session.setUserInfo(new CredentialsProviderUserInfo(session,
+					credentialsProvider));
+		}
 		configure(hc, session);
 		return session;
 	}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
index d10010f..34aa3db 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshSessionFactory.java
@@ -111,6 +111,8 @@ public static void setInstance(final SshSessionFactory newFactory) {
 	 * @param port
 	 *            port number the server is listening for connections on. May be <=
 	 *            0 to indicate the IANA registered port of 22 should be used.
+	 * @param credentialsProvider
+	 *            provider to support authentication, may be null.
 	 * @param fs
 	 *            the file system abstraction which will be necessary to
 	 *            perform certain file system operations.
@@ -119,14 +121,16 @@ public static void setInstance(final SshSessionFactory newFactory) {
 	 *             the session could not be created.
 	 */
 	public abstract Session getSession(String user, String pass, String host,
-			int port, FS fs) throws JSchException;
+			int port, CredentialsProvider credentialsProvider, FS fs)
+			throws JSchException;
 
 	/**
 	 * Close (or recycle) a session to a host.
 	 *
 	 * @param session
 	 *            a session previously obtained from this factory's
-	 *            {@link #getSession(String,String, String, int, FS)} method.s
+	 *            {@link #getSession(String,String, String, int, CredentialsProvider, FS)}
+	 *            method.
 	 */
 	public void releaseSession(final Session session) {
 		if (session.isConnected())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java
index f642ac1..81d233f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/SshTransport.java
@@ -128,7 +128,8 @@ protected void initSession() throws TransportException {
 		final String host = uri.getHost();
 		final int port = uri.getPort();
 		try {
-			sock = sch.getSession(user, pass, host, port, local.getFS());
+			sock = sch.getSession(user, pass, host, port,
+					getCredentialsProvider(), local.getFS());
 			if (!sock.isConnected())
 				sock.connect(tms);
 		} catch (JSchException je) {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
index 500cf0c..69eea0c 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Transport.java
@@ -558,6 +558,9 @@ private static String findTrackingRefName(final String remoteName,
 	/** Pack configuration used by this transport to make pack file. */
 	private PackConfig packConfig;
 
+	/** Assists with authentication the connection. */
+	private CredentialsProvider credentialsProvider;
+
 	/**
 	 * Create a new transport instance.
 	 *
@@ -574,6 +577,7 @@ protected Transport(final Repository local, final URIish uri) {
 		this.local = local;
 		this.uri = uri;
 		this.checkFetchedObjects = tc.isFsckObjects();
+		this.credentialsProvider = CredentialsProvider.getDefault();
 	}
 
 	/**
@@ -822,6 +826,26 @@ public void setPackConfig(PackConfig pc) {
 	}
 
 	/**
+	 * A credentials provider to assist with authentication connections..
+	 *
+	 * @param credentialsProvider
+	 *            the credentials provider, or null if there is none
+	 */
+	public void setCredentialsProvider(CredentialsProvider credentialsProvider) {
+		this.credentialsProvider = credentialsProvider;
+	}
+
+	/**
+	 * The configured credentials provider.
+	 *
+	 * @return the credentials provider, or null if no credentials provider is
+	 *         associated with this transport.
+	 */
+	public CredentialsProvider getCredentialsProvider() {
+		return credentialsProvider;
+	}
+
+	/**
 	 * Fetch objects and refs from the remote repository to the local one.
 	 * <p>
 	 * This is a utility function providing standard fetch behavior. Local
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
index 39dedc6..9eb1d2d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransportHttp.java
@@ -367,10 +367,12 @@ private HttpURLConnection connect(final String service)
 					if (authMethod == HttpAuthMethod.NONE)
 						throw new TransportException(uri, MessageFormat.format(
 								JGitText.get().authenticationNotSupported, uri));
-					if (1 < authAttempts || uri.getUser() == null)
+					if (1 < authAttempts
+							|| !authMethod.authorize(uri,
+									getCredentialsProvider())) {
 						throw new TransportException(uri,
 								JGitText.get().notAuthorized);
-					authMethod.authorize(uri);
+					}
 					authAttempts++;
 					continue;
 
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UsernamePasswordCredentialsProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UsernamePasswordCredentialsProvider.java
new file mode 100644
index 0000000..235e4b4
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UsernamePasswordCredentialsProvider.java
@@ -0,0 +1,124 @@
+/*
+ * 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.transport;
+
+import java.util.Arrays;
+
+import org.eclipse.jgit.errors.UnsupportedCredentialItem;
+
+/**
+ * Simple {@link CredentialsProvider} that always uses the same information.
+ */
+public class UsernamePasswordCredentialsProvider extends CredentialsProvider {
+	private String username;
+
+	private char[] password;
+
+	/**
+	 * Initialize the provider with a single username and password.
+	 *
+	 * @param username
+	 * @param password
+	 */
+	public UsernamePasswordCredentialsProvider(String username, String password) {
+		this(username, password.toCharArray());
+	}
+
+	/**
+	 * Initialize the provider with a single username and password.
+	 *
+	 * @param username
+	 * @param password
+	 */
+	public UsernamePasswordCredentialsProvider(String username, char[] password) {
+		this.username = username;
+		this.password = password;
+	}
+
+	@Override
+	public boolean isInteractive() {
+		return false;
+	}
+
+	@Override
+	public boolean supports(CredentialItem... items) {
+		for (CredentialItem i : items) {
+			if (i instanceof CredentialItem.Username)
+				continue;
+
+			else if (i instanceof CredentialItem.Password)
+				continue;
+
+			else
+				return false;
+		}
+		return true;
+	}
+
+	@Override
+	public boolean get(URIish uri, CredentialItem... items)
+			throws UnsupportedCredentialItem {
+		for (CredentialItem i : items) {
+			if (i instanceof CredentialItem.Username)
+				((CredentialItem.Username) i).setValue(username);
+
+			else if (i instanceof CredentialItem.Password)
+				((CredentialItem.Password) i).setValue(password);
+
+			else
+				throw new UnsupportedCredentialItem(uri, i.getPromptText());
+		}
+		return true;
+	}
+
+	/** Destroy the saved username and password.. */
+	public void clear() {
+		username = null;
+
+		if (password != null) {
+			Arrays.fill(password, (char) 0);
+			password = null;
+		}
+	}
+}