Git development
 help / color / mirror / Atom feed
* [JGIT PATCH 05/23] Change walker based fetch to use TreeWalk's MutableObjectId accessor
From: Shawn O. Pearce @ 2008-12-25  2:11 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230171079-17156-5-git-send-email-spearce@spearce.org>

Fetch time is usually dominated by network traffic, but while marking
objects reachable locally we can still benefit from the reduction in
object allocations this variant of getObjectId offers.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../jgit/transport/WalkFetchConnection.java        |   29 +++++++++++--------
 1 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
index 91c5ea8..6300f10 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkFetchConnection.java
@@ -59,6 +59,7 @@
 import org.spearce.jgit.lib.AnyObjectId;
 import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.FileMode;
+import org.spearce.jgit.lib.MutableObjectId;
 import org.spearce.jgit.lib.ObjectChecker;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.PackIndex;
@@ -152,6 +153,8 @@
 	 */
 	private final Set<String> packsConsidered;
 
+	private final MutableObjectId idBuffer = new MutableObjectId();
+
 	/**
 	 * Errors received while trying to obtain an object.
 	 * <p>
@@ -300,16 +303,17 @@ private void processTree(final RevObject obj) throws TransportException {
 
 				switch (sType) {
 				case Constants.OBJ_BLOB:
-				case Constants.OBJ_TREE: {
-					final ObjectId sId = treeWalk.getObjectId(0);
-					needs(revWalk.lookupAny(sId, sType));
+				case Constants.OBJ_TREE:
+					treeWalk.getObjectId(idBuffer, 0);
+					needs(revWalk.lookupAny(idBuffer, sType));
 					continue;
-				}
+
 				default:
 					if (FileMode.GITLINK.equals(mode))
 						continue;
+					treeWalk.getObjectId(idBuffer, 0);
 					throw new CorruptObjectException("Invalid mode " + mode
-							+ " for " + treeWalk.getObjectId(0).name() + " "
+							+ " for " + idBuffer.name() + " "
 							+ treeWalk.getPathString() + " in "
 							+ obj.getId().name() + ".");
 				}
@@ -722,14 +726,14 @@ private void markTreeComplete(final RevTree tree) throws IOException {
 			final int sType = mode.getObjectType();
 
 			switch (sType) {
-			case Constants.OBJ_BLOB: {
-				final ObjectId sid = treeWalk.getObjectId(0);
-				revWalk.lookupAny(sid, sType).add(COMPLETE);
+			case Constants.OBJ_BLOB:
+				treeWalk.getObjectId(idBuffer, 0);
+				revWalk.lookupAny(idBuffer, sType).add(COMPLETE);
 				continue;
-			}
+
 			case Constants.OBJ_TREE: {
-				final ObjectId sid = treeWalk.getObjectId(0);
-				final RevObject o = revWalk.lookupAny(sid, sType);
+				treeWalk.getObjectId(idBuffer, 0);
+				final RevObject o = revWalk.lookupAny(idBuffer, sType);
 				if (!o.has(COMPLETE)) {
 					o.add(COMPLETE);
 					treeWalk.enterSubtree();
@@ -739,8 +743,9 @@ private void markTreeComplete(final RevTree tree) throws IOException {
 			default:
 				if (FileMode.GITLINK.equals(mode))
 					continue;
+				treeWalk.getObjectId(idBuffer, 0);
 				throw new CorruptObjectException("Invalid mode " + mode
-						+ " for " + treeWalk.getObjectId(0).name() + " "
+						+ " for " + idBuffer.name() + " "
 						+ treeWalk.getPathString() + " in " + tree.name() + ".");
 			}
 		}
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 03/23] Allow TreeWalk callers to pass a MutableObjectId to get the current id
From: Shawn O. Pearce @ 2008-12-25  2:10 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230171079-17156-3-git-send-email-spearce@spearce.org>

This avoids a memory allocation associated with getting the entry
object for each name.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/treewalk/TreeWalk.java    |   28 +++++++++++++++++++-
 1 files changed, 27 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java
index 26544b5..38a726b 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java
@@ -46,6 +46,7 @@
 import org.spearce.jgit.errors.StopWalkException;
 import org.spearce.jgit.lib.Constants;
 import org.spearce.jgit.lib.FileMode;
+import org.spearce.jgit.lib.MutableObjectId;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.revwalk.RevTree;
@@ -513,7 +514,8 @@ public FileMode getFileMode(final int nth) {
 	 * <p>
 	 * Using this method to compare ObjectId values between trees of this walker
 	 * is very inefficient. Applications should try to use
-	 * {@link #idEqual(int, int)} whenever possible.
+	 * {@link #idEqual(int, int)} or {@link #getObjectId(MutableObjectId, int)}
+	 * whenever possible.
 	 * <p>
 	 * Every tree supplies an object id, even if the tree does not contain the
 	 * current entry. In the latter case {@link ObjectId#zeroId()} is returned.
@@ -521,6 +523,7 @@ public FileMode getFileMode(final int nth) {
 	 * @param nth
 	 *            tree to obtain the object identifier from.
 	 * @return object identifier for the current tree entry.
+	 * @see #getObjectId(MutableObjectId, int)
 	 * @see #idEqual(int, int)
 	 */
 	public ObjectId getObjectId(final int nth) {
@@ -530,6 +533,29 @@ public ObjectId getObjectId(final int nth) {
 	}
 
 	/**
+	 * Obtain the ObjectId for the current entry.
+	 * <p>
+	 * Every tree supplies an object id, even if the tree does not contain the
+	 * current entry. In the latter case {@link ObjectId#zeroId()} is supplied.
+	 * <p>
+	 * Applications should try to use {@link #idEqual(int, int)} when possible
+	 * as it avoids conversion overheads.
+	 * 
+	 * @param out
+	 *            buffer to copy the object id into.
+	 * @param nth
+	 *            tree to obtain the object identifier from.
+	 * @see #idEqual(int, int)
+	 */
+	public void getObjectId(final MutableObjectId out, final int nth) {
+		final AbstractTreeIterator t = trees[nth];
+		if (t.matches == currentHead)
+			out.fromRaw(t.idBuffer(), t.idOffset());
+		else
+			out.clear();
+	}
+
+	/**
 	 * Compare two tree's current ObjectId values for equality.
 	 * 
 	 * @param nthA
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 04/23] Switch ObjectWalk to use the new MutableObjectId form in TreeWalk
From: Shawn O. Pearce @ 2008-12-25  2:11 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230171079-17156-4-git-send-email-spearce@spearce.org>

RevWalk already has an idBuffer field available, which is used by
the commit and tree parsers to hold an id they need to probe for
in the hash table.  We should be using that buffer during tree
entry iteration as it avoids object allocation.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/revwalk/ObjectWalk.java   |   20 ++++++++++++--------
 1 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java b/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java
index 69a20aa..454cb4a 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/revwalk/ObjectWalk.java
@@ -251,7 +251,8 @@ public RevObject nextObject() throws MissingObjectException,
 
 			switch (sType) {
 			case Constants.OBJ_BLOB: {
-				final RevObject o = lookupAny(treeWalk.getObjectId(0), sType);
+				treeWalk.getObjectId(idBuffer, 0);
+				final RevObject o = lookupAny(idBuffer, sType);
 				if ((o.flags & SEEN) != 0)
 					continue;
 				o.flags |= SEEN;
@@ -262,7 +263,8 @@ public RevObject nextObject() throws MissingObjectException,
 				return o;
 			}
 			case Constants.OBJ_TREE: {
-				final RevObject o = lookupAny(treeWalk.getObjectId(0), sType);
+				treeWalk.getObjectId(idBuffer, 0);
+				final RevObject o = lookupAny(idBuffer, sType);
 				if ((o.flags & SEEN) != 0)
 					continue;
 				o.flags |= SEEN;
@@ -276,8 +278,9 @@ public RevObject nextObject() throws MissingObjectException,
 			default:
 				if (FileMode.GITLINK.equals(mode))
 					continue;
+				treeWalk.getObjectId(idBuffer, 0);
 				throw new CorruptObjectException("Invalid mode " + mode
-						+ " for " + treeWalk.getObjectId(0).name() + " "
+						+ " for " + idBuffer.name() + " "
 						+ treeWalk.getPathString() + " in " + currentTree + ".");
 			}
 		}
@@ -390,13 +393,13 @@ private void markTreeUninteresting(final RevTree tree)
 
 			switch (sType) {
 			case Constants.OBJ_BLOB: {
-				final ObjectId sid = treeWalk.getObjectId(0);
-				lookupAny(sid, sType).flags |= UNINTERESTING;
+				treeWalk.getObjectId(idBuffer, 0);
+				lookupAny(idBuffer, sType).flags |= UNINTERESTING;
 				continue;
 			}
 			case Constants.OBJ_TREE: {
-				final ObjectId sid = treeWalk.getObjectId(0);
-				final RevObject subtree = lookupAny(sid, sType);
+				treeWalk.getObjectId(idBuffer, 0);
+				final RevObject subtree = lookupAny(idBuffer, sType);
 				if ((subtree.flags & UNINTERESTING) == 0) {
 					subtree.flags |= UNINTERESTING;
 					treeWalk.enterSubtree();
@@ -406,8 +409,9 @@ private void markTreeUninteresting(final RevTree tree)
 			default:
 				if (FileMode.GITLINK.equals(mode))
 					continue;
+				treeWalk.getObjectId(idBuffer, 0);
 				throw new CorruptObjectException("Invalid mode " + mode
-						+ " for " + treeWalk.getObjectId(0).name() + " "
+						+ " for " + idBuffer.name() + " "
 						+ treeWalk.getPathString() + " in " + tree + ".");
 			}
 		}
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 02/23] Add MutableObjectId.clear() to set the id to zeroId
From: Shawn O. Pearce @ 2008-12-25  2:10 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230171079-17156-2-git-send-email-spearce@spearce.org>

This makes it easier to set any MutableObjectId to the same value
as the zeroId.  Its logical to ask for a ".clear()" invocation when
you are writing application code, and its more efficient for the
JIT if we do 5 zero writes then to perform a copy from the zeroId
singleton.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/lib/MutableObjectId.java  |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/MutableObjectId.java b/org.spearce.jgit/src/org/spearce/jgit/lib/MutableObjectId.java
index 5b16345..fadebab 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/MutableObjectId.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/MutableObjectId.java
@@ -66,6 +66,15 @@ MutableObjectId(MutableObjectId src) {
 		this.w5 = src.w5;
 	}
 
+	/** Make this id match {@link ObjectId#zeroId()}. */
+	public void clear() {
+		w1 = 0;
+		w2 = 0;
+		w3 = 0;
+		w4 = 0;
+		w5 = 0;
+	}
+
 	/**
 	 * Convert an ObjectId from raw binary representation.
 	 * 
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 01/23] Improve hit performance on the UnpackedObjectCache
From: Shawn O. Pearce @ 2008-12-25  2:10 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230171079-17156-1-git-send-email-spearce@spearce.org>

If the JVM cleared one of our SoftReferences we "leaked" the space in
the cache that the entry occupied.  Over time this meant we lost room
in the cache and didn't have a way to recover that when we replaced
the evicted entry.

The hash function was also too complex for the hit ratio we were
getting.  The old function on one of my linux-2.6 clones was giving
us <7% hit ratio; this new function is a little simpler to compute
and is getting ~11%.  Increasing the size of the hash table helps
matters considerably.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../org/spearce/jgit/lib/UnpackedObjectCache.java  |   21 +++++++++----------
 1 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectCache.java b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectCache.java
index ee6a680..677b3a7 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectCache.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectCache.java
@@ -40,17 +40,14 @@
 import java.lang.ref.SoftReference;
 
 class UnpackedObjectCache {
-	private static final int CACHE_SZ = 256;
+	private static final int CACHE_SZ = 1024;
 
 	private static final int MB = 1024 * 1024;
 
 	private static final SoftReference<Entry> DEAD;
 
-	private static int hash(final WindowedFile pack, final long position) {
-		int h = pack.hash + (int) position;
-		h += h >>> 16;
-		h += h >>> 8;
-		return h & (CACHE_SZ - 1);
+	private static int hash(final long position) {
+		return (((int) position) << 22) >>> 22;
 	}
 
 	private static int maxByteCount;
@@ -80,7 +77,7 @@ static synchronized void reconfigure(final int dbLimit) {
 	}
 
 	static synchronized Entry get(final WindowedFile pack, final long position) {
-		final Slot e = cache[hash(pack, position)];
+		final Slot e = cache[hash(position)];
 		if (e.provider == pack && e.position == position) {
 			final Entry buf = e.data.get();
 			if (buf != null) {
@@ -96,7 +93,7 @@ static synchronized void store(final WindowedFile pack,
 		if (data.length > maxByteCount)
 			return; // Too large to cache.
 
-		final Slot e = cache[hash(pack, position)];
+		final Slot e = cache[hash(position)];
 		clearEntry(e);
 
 		openByteCount += data.length;
@@ -104,6 +101,7 @@ static synchronized void store(final WindowedFile pack,
 
 		e.provider = pack;
 		e.position = position;
+		e.sz = data.length;
 		e.data = new SoftReference<Entry>(new Entry(data, objectType));
 		moveToHead(e);
 	}
@@ -155,11 +153,10 @@ private static void unlink(final Slot e) {
 	}
 
 	private static void clearEntry(final Slot e) {
-		final Entry old = e.data.get();
-		if (old != null)
-			openByteCount -= old.data.length;
+		openByteCount -= e.sz;
 		e.provider = null;
 		e.data = DEAD;
+		e.sz = 0;
 	}
 
 	private UnpackedObjectCache() {
@@ -186,6 +183,8 @@ Entry(final byte[] aData, final int aType) {
 
 		long position;
 
+		int sz;
+
 		SoftReference<Entry> data = DEAD;
 	}
 }
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 00/18] Misc. performance tweaks
From: Shawn O. Pearce @ 2008-12-25  2:10 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

Cloning linux-2.6.git through JGit was painful at best.  I found
and fixed some small bottlenecks after a day of profiling and
experimentation, but we're still slower than C git.

With this series I managed to drop the time for "git clone --bare"
over git:// using "jgit daemon" server and "C git" client.
Any difference between jgit and "C git" is in the server side.

  before:  7m42.488s
  after :  2m33.882s
  C git :  1m26.158s     ("git daemon" server)


So I'm still seeing a major bottleneck that I can't quite fix.

Object enumeration (aka "Counting ...") takes too long, because we
spend a huge amount of time unpacking delta chains for trees so we
can enumerate their referenced items.

Our UnpackedObjectCache gets <4% hit ratio when doing the trees
for linux-2.6.git.  Increasing the cache doesn't have a noticable
improvement on performance.

I tried rewriting UnpackedObjectCache to permit multiple objects
per hash bucket.  Even with that (and the maximum chain length
per bucket not exceeding 4 items) our hit ratio was still <5%,
so I tossed that implementation out.

"jgit rev-list --objects" vs. "git rev-list --objects" is a huge
difference, about 1m difference.  That's most of the time difference
I noted above between jgit and C git on the server side.

So with this series, we're better.  Its actually almost tolerable
to clone linux-2.6 through a jgit backed server.


Shawn O. Pearce (23):
  Improve hit performance on the UnpackedObjectCache
  Add MutableObjectId.clear() to set the id to zeroId
  Allow TreeWalk callers to pass a MutableObjectId to get the current
    id
  Switch ObjectWalk to use the new MutableObjectId form in TreeWalk
  Change walker based fetch to use TreeWalk's MutableObjectId accessor
  Reduce garbage allocation when using TreeWalk
  Switch ObjectWalk to use a naked CanonicalTreeParser because its
    faster
  Remove the unused PackFile.get(ObjectId) form
  Remove getId from ObjectLoader API as its unnecessary overhead
  Make mmap mode more reliable by forcing GC at the correct spot
  Rewrite WindowCache to use a hash table
  Change ByteArrayWindow to read content outside of WindowCache's lock
  Dispose of RevCommit buffers when they aren't used in PackWriter
  Don't unpack delta chains while writing a pack from a pack v1 index
  Don't unpack delta chains while converting delta to whole object
  Defer parsing of the ObjectId while walking a PackIndex Iterator
  Only do one getCachedBytes per whole object written
  Correctly use a long for the offsets within a generated pack
  Allow more direct access to determine isWritten
  Move "wantWrite" field of ObjectToPack into the flags field
  Use an ArrayList for the reuseLoader collection in PackWriter
  Don't cut off existing delta chains if we are reusing deltas
  Correctly honor the thin parameter to PackWriter.writePack

 .../jgit/pgm/opt/AbstractTreeIteratorHandler.java  |    6 +-
 .../tst/org/spearce/jgit/lib/PackIndexTest.java    |    4 +-
 .../tst/org/spearce/jgit/lib/PackWriterTest.java   |   14 +-
 .../tst/org/spearce/jgit/lib/T0004_PackReader.java |    4 +-
 .../jgit/errors/CorruptObjectException.java        |   12 +
 .../src/org/spearce/jgit/lib/ByteArrayWindow.java  |   31 ++
 .../src/org/spearce/jgit/lib/ByteBufferWindow.java |   17 +
 .../src/org/spearce/jgit/lib/ByteWindow.java       |   20 ++-
 .../src/org/spearce/jgit/lib/Constants.java        |    2 +-
 .../spearce/jgit/lib/DeltaPackedObjectLoader.java  |    3 +-
 .../src/org/spearce/jgit/lib/MutableObjectId.java  |    9 +
 .../src/org/spearce/jgit/lib/ObjectLoader.java     |   38 ---
 .../src/org/spearce/jgit/lib/PackFile.java         |   49 +---
 .../src/org/spearce/jgit/lib/PackIndex.java        |   48 ++--
 .../src/org/spearce/jgit/lib/PackIndexV1.java      |   20 +-
 .../src/org/spearce/jgit/lib/PackIndexV2.java      |   27 +-
 .../src/org/spearce/jgit/lib/PackWriter.java       |   63 +++--
 .../src/org/spearce/jgit/lib/Repository.java       |   29 +--
 .../org/spearce/jgit/lib/UnpackedObjectCache.java  |   21 +-
 .../org/spearce/jgit/lib/UnpackedObjectLoader.java |   12 +-
 .../spearce/jgit/lib/WholePackedObjectLoader.java  |    3 +-
 .../src/org/spearce/jgit/lib/WindowCache.java      |  323 ++++++++++++--------
 .../src/org/spearce/jgit/lib/WindowCursor.java     |   16 +
 .../src/org/spearce/jgit/lib/WindowedFile.java     |   61 +++--
 .../src/org/spearce/jgit/revwalk/ObjectWalk.java   |   51 ++--
 .../src/org/spearce/jgit/revwalk/RevWalk.java      |    8 +-
 .../spearce/jgit/transport/PackedObjectInfo.java   |    2 +-
 .../src/org/spearce/jgit/transport/UploadPack.java |    1 +
 .../jgit/transport/WalkFetchConnection.java        |   48 ++-
 .../jgit/treewalk/AbstractTreeIterator.java        |   48 +++
 .../spearce/jgit/treewalk/CanonicalTreeParser.java |   85 +++++-
 .../src/org/spearce/jgit/treewalk/TreeWalk.java    |   88 +++++-
 .../spearce/jgit/util/CountingOutputStream.java    |    5 +-
 33 files changed, 752 insertions(+), 416 deletions(-)

^ permalink raw reply

* git rev-list with --name-status?
From: skillzero @ 2008-12-24 22:53 UTC (permalink / raw)
  To: git

Is there a way to print the equivalent of --name-status with git
rev-list? The post-receive script that comes with git for sending
comment emails does this to generate the commit log:

git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
	git rev-list --pretty --stdin $oldrev..$newrev

I'd like to also include the output of --name-status so the email
shows which files were changed by each commit (rather than just a
summary at the end since our pushes sometimes have a lot of commits in
them).

git rev-list doesn't seem to support --name-status and git log doesn't
seem to support --stdin. Is there a better way to do what I want?

^ permalink raw reply

* Re: Git with Hudson
From: Stephen Haberman @ 2008-12-24 21:04 UTC (permalink / raw)
  To: R. Tyler Ballance; +Cc: Tim Visher, Indy Nagpal, git
In-Reply-To: <1230152365.14882.48.camel@starfruit>


> > Here's the "git2" Hudson plugin that worked well for us:
> > 
> > http://github.com/stephenh/hudson-git2
> > 
> > There is a git.hpi file checked into the target directory so that you
> > don't have to build it yourself.
> 
> I decided that I'd check this out, as the existing Git plugin chokes on
> our git-rev-list(1) on our enormous Git repository.
> 
> Correct me if I'm wrong, but did you actually upload or check in the
> target/ directory? Your .markdown references target/git.hpi as well, but
> I can't seem to find it :-/

Ack, sorry, just added it.

- Stephen

^ permalink raw reply

* Re: Git with Hudson
From: R. Tyler Ballance @ 2008-12-24 20:59 UTC (permalink / raw)
  To: Stephen Haberman; +Cc: Tim Visher, Indy Nagpal, git
In-Reply-To: <20081222210018.f21d9e07.stephen@exigencecorp.com>

[-- Attachment #1: Type: text/plain, Size: 679 bytes --]

On Mon, 2008-12-22 at 21:00 -0600, Stephen Haberman wrote:
> > I'm interested!  Please publish away!
> 
> Here's the "git2" Hudson plugin that worked well for us:
> 
> http://github.com/stephenh/hudson-git2
> 
> There is a git.hpi file checked into the target directory so that you
> don't have to build it yourself.

I decided that I'd check this out, as the existing Git plugin chokes on
our git-rev-list(1) on our enormous Git repository.

Correct me if I'm wrong, but did you actually upload or check in the
target/ directory? Your .markdown references target/git.hpi as well, but
I can't seem to find it :-/


Cheers
-- 
-R. Tyler Ballance
Slide, Inc.

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* [PATCH] builtin-shortlog.c: do not unnecessarily strdup before insertion in list
From: Adeodato Simó @ 2008-12-24 16:34 UTC (permalink / raw)
  To: git, gitster; +Cc: Adeodato Simó

The log->list always has "strdup_strings" activated, hence strdup'ing
namebuf was unnecessary. This change also removes a latent memory leak
in the old code.

Signed-off-by: Adeodato Simó <dato@net.com.org.es>
---
 builtin-shortlog.c |    5 +----
 1 files changed, 1 insertions(+), 4 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 4c5d761..90e76ae 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -67,12 +67,9 @@ static void insert_one_record(struct shortlog *log,
 		snprintf(namebuf + len, room, " %.*s", maillen, boemail);
 	}
 
-	buffer = xstrdup(namebuf);
-	item = string_list_insert(buffer, &log->list);
+	item = string_list_insert(namebuf, &log->list);
 	if (item->util == NULL)
 		item->util = xcalloc(1, sizeof(struct string_list));
-	else
-		free(buffer);
 
 	/* Skip any leading whitespace, including any blank lines. */
 	while (*oneline && isspace(*oneline))
-- 
1.6.0.4

^ permalink raw reply related

* [PATCH] builtin-shortlog.c: use string_list_append() instead of duplicating its code
From: Adeodato Simó @ 2008-12-24 16:34 UTC (permalink / raw)
  To: git, gitster; +Cc: Adeodato Simó

Also, when clearing the "onelines" string lists, do not free the "util"
member: with string_list_append() is not initialized to any value (and
was being initialized to NULL previously anyway).

NB: The duplicated code in builtin-shortlog.c predated the appearance of
string_list_append().

Signed-off-by: Adeodato Simó <dato@net.com.org.es>
---
 builtin-shortlog.c |   14 ++------------
 1 files changed, 2 insertions(+), 12 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index d03f14f..4c5d761 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -36,7 +36,6 @@ static void insert_one_record(struct shortlog *log,
 	const char *dot3 = log->common_repo_prefix;
 	char *buffer, *p;
 	struct string_list_item *item;
-	struct string_list *onelines;
 	char namebuf[1024];
 	size_t len;
 	const char *eol;
@@ -104,16 +103,7 @@ static void insert_one_record(struct shortlog *log,
 		}
 	}
 
-	onelines = item->util;
-	if (onelines->nr >= onelines->alloc) {
-		onelines->alloc = alloc_nr(onelines->nr);
-		onelines->items = xrealloc(onelines->items,
-				onelines->alloc
-				* sizeof(struct string_list_item));
-	}
-
-	onelines->items[onelines->nr].util = NULL;
-	onelines->items[onelines->nr++].string = buffer;
+	string_list_append(buffer, item->util);
 }
 
 static void read_from_stdin(struct shortlog *log)
@@ -323,7 +313,7 @@ void shortlog_output(struct shortlog *log)
 		}
 
 		onelines->strdup_strings = 1;
-		string_list_clear(onelines, 1);
+		string_list_clear(onelines, 0);
 		free(onelines);
 		log->list.items[i].util = NULL;
 	}
-- 
1.6.0.4

^ permalink raw reply related

* Re: [PATCH] strbuf_readlink semantics update.
From: René Scharfe @ 2008-12-24 15:20 UTC (permalink / raw)
  To: Pierre Habouzit; +Cc: Linus Torvalds, git, Junio C Hamano
In-Reply-To: <20081224101146.GA10008@artemis.corp>

Pierre Habouzit schrieb:
> On Tue, Dec 23, 2008 at 06:16:01PM +0000, Linus Torvalds wrote:
>>
>> On Tue, 23 Dec 2008, Pierre Habouzit wrote:
>>> when readlink fails, the strbuf shall not be destroyed. It's not how
>>> read_file_or_gitlink works for example.
>> I disagree.
>>
>> This patch just makes things worse. Just leave the "strbuf_release()" in 
>> _one_ place.
> 
> The "problem" is that the strbuf API usually works that way: functions
> append things to a buffer, or do nothing, but always keep the buffer in
> a state where you can append more stuff to it.
> 
> If read_file_or_gitlink or strbuf_readlink destroy the buffer, then you
> break the second expectation people (should) have about the strbuf API.

The "append or do nothing" rule is broken by strbuf_getline(), but I agree
to your reasoning.  How about refining this rule a bit to "do your thing
and roll back changes if an error occurs"?  I think it's not worth to undo
allocation extensions, but making reverting first time allocations seems
like a good idea.  Something like this?

René


PS: only nine lines! ;-)


 strbuf.c |   17 +++++++++++++----
 1 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/strbuf.c b/strbuf.c
index bdf4954..6ed0684 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -256,18 +256,21 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
 size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
 {
 	size_t res;
+	size_t oldalloc = sb->alloc;
 
 	strbuf_grow(sb, size);
 	res = fread(sb->buf + sb->len, 1, size, f);
-	if (res > 0) {
+	if (res > 0)
 		strbuf_setlen(sb, sb->len + res);
-	}
+	else if (res < 0 && oldalloc == 0)
+		strbuf_release(sb);
 	return res;
 }
 
 ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
 {
 	size_t oldlen = sb->len;
+	size_t oldalloc = sb->alloc;
 
 	strbuf_grow(sb, hint ? hint : 8192);
 	for (;;) {
@@ -275,7 +278,10 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
 
 		cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
 		if (cnt < 0) {
-			strbuf_setlen(sb, oldlen);
+			if (oldalloc == 0)
+				strbuf_release(sb);
+			else
+				strbuf_setlen(sb, oldlen);
 			return -1;
 		}
 		if (!cnt)
@@ -292,6 +298,8 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
 
 int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
 {
+	size_t oldalloc = sb->alloc;
+
 	if (hint < 32)
 		hint = 32;
 
@@ -311,7 +319,8 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
 		/* .. the buffer was too small - try again */
 		hint *= 2;
 	}
-	strbuf_release(sb);
+	if (oldalloc == 0)
+		strbuf_release(sb);
 	return -1;
 }
 

^ permalink raw reply related

* Re: What's cooking in git.git (Dec 2008, #03; Sun, 21)
From: Johannes Schindelin @ 2008-12-24 11:55 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git
In-Reply-To: <20081223173438.GA25699@coredump.intra.peff.net>

Hi,

On Tue, 23 Dec 2008, Jeff King wrote:

> On Tue, Dec 23, 2008 at 05:52:54PM +0100, Johannes Schindelin wrote:
> 
> > However, note that without something like core.notesref you will never 
> > be able to have private and public notes.
> > 
> > And I very much want to have private notes _and_ public notes on the 
> > very same commits of the very same branches.
> 
> Right. I think core.notesref doesn't go far enough, because it doesn't
> provide a way to talk about notes from two sources at the same time.
> Like:
> 
>   git log --pretty=format:'%N(my-private-notes:foo) %N(public-notes:bar)'

Ah.  That should be easy to do incrementally, by adding a ref_name 
parameter to the functions in notes.h, which can be NULL (falling back to 
the default ref).

> > And while I am almost sure that there is a stupid bug lurking that 
> > will kick the performance again, I think the basic design is sound, 
> > and it should be easy to modify no matter which way you want to change 
> > the behavior with regards to trees/blobs or refs.
> 
> I agree that the data structure is sound, so I can probably work on top
> of what you posted, too. I was planning on doing git-notes in C, though.

Sure.  git-notes.sh is only in shell script to provide a proof-of-concept, 
and an example how to have an ultra-narrow "checkout" of the notes ref's 
tree.

Ciao,
Dscho

^ permalink raw reply

* Re: [PATCH] strbuf_readlink semantics update.
From: Pierre Habouzit @ 2008-12-24 10:11 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: git, Junio C Hamano
In-Reply-To: <alpine.LFD.2.00.0812231009220.3535@localhost.localdomain>

[-- Attachment #1: Type: text/plain, Size: 1420 bytes --]

On Tue, Dec 23, 2008 at 06:16:01PM +0000, Linus Torvalds wrote:
> 
> 
> On Tue, 23 Dec 2008, Pierre Habouzit wrote:
> >
> > when readlink fails, the strbuf shall not be destroyed. It's not how
> > read_file_or_gitlink works for example.
> 
> I disagree.
> 
> This patch just makes things worse. Just leave the "strbuf_release()" in 
> _one_ place.

The "problem" is that the strbuf API usually works that way: functions
append things to a buffer, or do nothing, but always keep the buffer in
a state where you can append more stuff to it.

If read_file_or_gitlink or strbuf_readlink destroy the buffer, then you
break the second expectation people (should) have about the strbuf API.

The reason is that if you built things in the buffer, you really don't
want it to be undone just because the last bit you add went wrong for
some reason. Or if you have a buffer that is reused in a loop, you don't
want the buffer you allocated to be dropped just because one error
occurred.


Alternatively, we could pass a flag to tell function performing reads
(fread, read, readlink, whatever) that those should destroy the buffer
on error or just report it. I don't really know. It sounds like
over-engineering though.


-- 
·O·  Pierre Habouzit
··O                                                madcoder@debian.org
OOO                                                http://www.madism.org

[-- Attachment #2: Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply

* Re: [PATCH] Simplified GIT usage guide
From: Valdis.Kletnieks @ 2008-12-24  4:35 UTC (permalink / raw)
  To: paulmck
  Cc: Junio C Hamano, Johannes Schindelin, David Howells, torvalds, git,
	linux-kernel
In-Reply-To: <20081219012723.GI6912@linux.vnet.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 941 bytes --]

On Thu, 18 Dec 2008 17:27:23 PST, "Paul E. McKenney" said:

> I would be OK with it being in linux-2.6.git rather than git.git,
> if that helps.  Certainly there seems to be room for a description
> of how to use git within the Linux community.

What might help a lot of people (me, for one) would be a cookbook listing
how to do things that those of us on the fringe might want to do. For example:

"type this to pull Linus's tree, and this to bisect it" (I already know how
to do this one, actually)

"type this to pull a linux-next tree, and this to bisect it" (Last time I tried,
the pull went OK, but I couldn't figure out how to give 'git bisect' a
start/end commit that it was happy with).

"'git log foo/bar/baz.c' is your friend if you're chasing a recently added
bug/regression in baz.c"

Hmm... it occurs to me that my only actual use for git is to find a commit
ID so when I whinge to a developer, we're on the same page... ;)



[-- Attachment #2: Type: application/pgp-signature, Size: 226 bytes --]

^ permalink raw reply

* Re: [PATCH 2/2] gitweb: support hiding projects from user-visible lists
From: Matt McCutchen @ 2008-12-24  1:40 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: git
In-Reply-To: <m3ljujg2eh.fsf@localhost.localdomain>

On Sat, 2008-12-13 at 14:02 -0800, Jakub Narebski wrote:
> Cannot you do this with new $export_auth_hook gitweb configuration
> variable, added by Alexander Gavrilov in 
>    dd7f5f1 (gitweb: Add a per-repository authorization hook.)
> It is used in check_export_ok subroutine, and is is checked also when
> getting list of project from file
> 
> >From gitweb/INSTALL
> 
>   - Finally, it is possible to specify an arbitrary perl subroutine that
>     will be called for each project to determine if it can be exported.
>     The subroutine receives an absolute path to the project as its only
>     parameter.
> 
>     For example, if you use mod_perl to run the script, and have dumb
>     http protocol authentication configured for your repositories, you
>     can use the following hook to allow access only if the user is
>     authorized to read the files:
> 
>       $export_auth_hook = sub {
>           use Apache2::SubRequest ();
>           use Apache2::Const -compile => qw(HTTP_OK);
>           my $path = "$_[0]/HEAD";
>           my $r    = Apache2::RequestUtil->request;
>           my $sub  = $r->lookup_file($path);
>           return $sub->filename eq $path
>               && $sub->status == Apache2::Const::HTTP_OK;
>       };

$export_auth_hook would work, and it would have the nice (but not
essential) feature of including private projects in the list shown to
suitably authenticated users.  The only problem is that my Web host
doesn't support mod_perl.  Is there a practical way to accomplish the
same thing as the above example in a CGI script?  I would like to avoid
reimplementing Apache authentication-checking functionality if at all
possible.

-- 
Matt

^ permalink raw reply

* Re: Git merge conflicts and encoding of logs
From: Junichi Uekawa @ 2008-12-23 23:37 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Johannes Sixt, Junichi Uekawa, git
In-Reply-To: <7vlju7fbqu.fsf@gitster.siamese.dyndns.org>

At Tue, 23 Dec 2008 02:04:57 -0800,
Junio C Hamano wrote:
> 
> Johannes Sixt <j.sixt@viscovery.net> writes:
> 
> > Junio C Hamano schrieb:
> >> <<<<<<< HEAD:foo
> >> これはサイドブランチの変更です。
> >> やはり JIS コードで書いてます。
> >> =======
> >> 日本語のファイルです。
> >> JIS コードで書いてます。
> >>>>>>>>> master:foo
> >> 
> >> The above will probably come out as UTF-8 in this mail text, but the point
> >> is that the confict side markers do not have anything but filename and the
> >> branch name.  I am still scratching my head trying to see where in the
> >> merge-recursive codepath you got snippet of log message.
> >
> > Try rebase -i instead of merge: This should put summary lines onto the
> > conflict markers.
> 
> Ah, that's cherry-pick.
> 
> The fix should be around the area this weather-balloon patch touches.
> 
> Note that this does not correctly work yet, and it seems that somewhere the
> string is truncated.


Hi, I've read patches from Alexander Gavrilov which try to support
per-file encoding in gitattributes (encoding), I assume such features
are not in yet.

My git repository has mixture of iso-2022-jp(platex source), EUC-JP
(some graph source code and other things), UTF-8, so log output format
unification to iso-2022-jp is not ideal. That way, I'd have to guess
which file is going to have a conflict through cherry-pick and define
the logoutput encoding.



regards,
	junichi
-- 
dancer@{netfort.gr.jp,debian.org}

^ permalink raw reply

* [JGIT PATCH 5/5] Switch local fetch connection to use our own UploadPack
From: Shawn O. Pearce @ 2008-12-23 22:56 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230073007-17337-5-git-send-email-spearce@spearce.org>

Just like with push, we now use our own version of upload pack to
perform a fetch from a local repository.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../jgit/transport/BasePackFetchConnection.java    |    2 +
 .../org/spearce/jgit/transport/TransportLocal.java |   96 +++++++++++++++++++-
 2 files changed, 95 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
index 1fe504b..19ac161 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
@@ -86,6 +86,8 @@
 	 */
 	private static final int MAX_HAVES = 256;
 
+	protected static final int MAX_CLIENT_BUFFER = MAX_HAVES * 46 + 1024;
+
 	static final String OPTION_INCLUDE_TAG = "include-tag";
 
 	static final String OPTION_MULTI_ACK = "multi_ack";
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
index b5dd5fc..17d95c2 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
@@ -86,7 +86,10 @@ TransportLocal(final Repository local, final URIish uri) {
 
 	@Override
 	public FetchConnection openFetch() throws TransportException {
-		return new LocalFetchConnection();
+		final String up = getOptionUploadPack();
+		if ("git-upload-pack".equals(up) || "git upload-pack".equals(up))
+			return new InternalLocalFetchConnection();
+		return new ForkLocalFetchConnection();
 	}
 
 	@Override
@@ -130,10 +133,97 @@ protected Process startProcessWithErrStream(final String cmd)
 		}
 	}
 
-	class LocalFetchConnection extends BasePackFetchConnection {
+	class InternalLocalFetchConnection extends BasePackFetchConnection {
+		private Thread worker;
+
+		InternalLocalFetchConnection() throws TransportException {
+			super(TransportLocal.this);
+
+			final Repository dst;
+			try {
+				dst = new Repository(remoteGitDir);
+			} catch (IOException err) {
+				throw new TransportException(uri, "not a git directory");
+			}
+
+			final PipedInputStream in_r;
+			final PipedOutputStream in_w;
+
+			final PipedInputStream out_r;
+			final PipedOutputStream out_w;
+			try {
+				in_r = new PipedInputStream();
+				in_w = new PipedOutputStream(in_r);
+
+				out_r = new PipedInputStream() {
+					// The client (BasePackFetchConnection) can write
+					// a huge burst before it reads again. We need to
+					// force the buffer to be big enough, otherwise it
+					// will deadlock both threads.
+					{
+						buffer = new byte[MAX_CLIENT_BUFFER];
+					}
+				};
+				out_w = new PipedOutputStream(out_r);
+			} catch (IOException err) {
+				dst.close();
+				throw new TransportException(uri, "cannot connect pipes", err);
+			}
+
+			worker = new Thread("JGit-Upload-Pack") {
+				public void run() {
+					try {
+						final UploadPack rp = new UploadPack(dst);
+						rp.upload(out_r, in_w, null);
+					} catch (IOException err) {
+						// Client side of the pipes should report the problem.
+						err.printStackTrace();
+					} catch (RuntimeException err) {
+						// Clients side will notice we went away, and report.
+						err.printStackTrace();
+					} finally {
+						try {
+							out_r.close();
+						} catch (IOException e2) {
+							// Ignore close failure, we probably crashed above.
+						}
+
+						try {
+							in_w.close();
+						} catch (IOException e2) {
+							// Ignore close failure, we probably crashed above.
+						}
+
+						dst.close();
+					}
+				}
+			};
+			worker.start();
+
+			init(in_r, out_w);
+			readAdvertisedRefs();
+		}
+
+		@Override
+		public void close() {
+			super.close();
+
+			if (worker != null) {
+				try {
+					worker.join();
+				} catch (InterruptedException ie) {
+					// Stop waiting and return anyway.
+				} finally {
+					worker = null;
+				}
+			}
+		}
+	}
+
+	class ForkLocalFetchConnection extends BasePackFetchConnection {
 		private Process uploadPack;
 
-		LocalFetchConnection() throws TransportException {
+		ForkLocalFetchConnection() throws TransportException {
 			super(TransportLocal.this);
 			uploadPack = startProcessWithErrStream(getOptionUploadPack());
 			init(uploadPack.getInputStream(), uploadPack.getOutputStream());
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 3/5] Implement "jgit upload-pack" to support fetching from jgit
From: Shawn O. Pearce @ 2008-12-23 22:56 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230073007-17337-3-git-send-email-spearce@spearce.org>

All current options, with the exception of shallow fetch, are
supported by this implementation.

"jgit upload-pack" is included to support remote server side
execution.  "jgit daemon" has also had the upload-pack service
added to its service table, making fetch over git:// possible.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../services/org.spearce.jgit.pgm.TextBuiltin      |    1 +
 .../src/org/spearce/jgit/pgm/UploadPack.java       |   67 +++
 .../src/org/spearce/jgit/lib/PackWriter.java       |   85 +++-
 .../jgit/transport/BasePackFetchConnection.java    |    2 +
 .../src/org/spearce/jgit/transport/Daemon.java     |   36 ++-
 .../org/spearce/jgit/transport/PacketLineOut.java  |   19 +-
 .../jgit/transport/SideBandInputStream.java        |    6 +-
 .../jgit/transport/SideBandOutputStream.java       |   93 ++++
 .../jgit/transport/SideBandProgressMonitor.java    |  150 ++++++
 .../src/org/spearce/jgit/transport/UploadPack.java |  491 ++++++++++++++++++++
 10 files changed, 915 insertions(+), 35 deletions(-)
 create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java

diff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin
index 40177f9..1ba29e6 100644
--- a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin
+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin
@@ -16,6 +16,7 @@ org.spearce.jgit.pgm.RevList
 org.spearce.jgit.pgm.Rm
 org.spearce.jgit.pgm.ShowRev
 org.spearce.jgit.pgm.Tag
+org.spearce.jgit.pgm.UploadPack
 org.spearce.jgit.pgm.Version
 
 org.spearce.jgit.pgm.debug.MakeCacheTree
diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java
new file mode 100644
index 0000000..d6f6d7c
--- /dev/null
+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.pgm;
+
+import java.io.File;
+
+import org.kohsuke.args4j.Argument;
+import org.spearce.jgit.lib.Repository;
+
+@Command(common = false, usage = "Server side backend for 'jgit fetch'")
+class UploadPack extends TextBuiltin {
+	@Argument(index = 0, required = true, metaVar = "DIRECTORY", usage = "Repository to read from")
+	File gitdir;
+
+	@Override
+	protected final boolean requiresRepository() {
+		return false;
+	}
+
+	@Override
+	protected void run() throws Exception {
+		final org.spearce.jgit.transport.UploadPack rp;
+
+		if (new File(gitdir, ".git").isDirectory())
+			gitdir = new File(gitdir, ".git");
+		db = new Repository(gitdir);
+		if (!db.getObjectsDirectory().isDirectory())
+			throw die("'" + gitdir.getPath() + "' not a git repository");
+		rp = new org.spearce.jgit.transport.UploadPack(db);
+		rp.upload(System.in, System.out, System.err);
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
index 32394f2..89460f2 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
@@ -173,7 +173,9 @@
 
 	private final Deflater deflater;
 
-	private final ProgressMonitor monitor;
+	private ProgressMonitor initMonitor;
+
+	private ProgressMonitor writeMonitor;
 
 	private final byte[] buf = new byte[16384]; // 16 KB
 
@@ -200,18 +202,41 @@
 	 * <p>
 	 * Objects for packing are specified in {@link #preparePack(Iterator)} or
 	 * {@link #preparePack(Collection, Collection, boolean, boolean)}.
-	 *
+	 * 
 	 * @param repo
 	 *            repository where objects are stored.
 	 * @param monitor
 	 *            operations progress monitor, used within
 	 *            {@link #preparePack(Iterator)},
-	 *            {@link #preparePack(Collection, Collection, boolean, boolean)},
-	 *            or {@link #writePack(OutputStream)}.
+	 *            {@link #preparePack(Collection, Collection, boolean, boolean)}
+	 *            , or {@link #writePack(OutputStream)}.
 	 */
 	public PackWriter(final Repository repo, final ProgressMonitor monitor) {
+		this(repo, monitor, monitor);
+	}
+
+	/**
+	 * Create writer for specified repository.
+	 * <p>
+	 * Objects for packing are specified in {@link #preparePack(Iterator)} or
+	 * {@link #preparePack(Collection, Collection, boolean, boolean)}.
+	 * 
+	 * @param repo
+	 *            repository where objects are stored.
+	 * @param imonitor
+	 *            operations progress monitor, used within
+	 *            {@link #preparePack(Iterator)},
+	 *            {@link #preparePack(Collection, Collection, boolean, boolean)}
+	 *            ;
+	 * @param wmonitor
+	 *            operations progress monitor, used within
+	 *            {@link #writePack(OutputStream)}.
+	 */
+	public PackWriter(final Repository repo, final ProgressMonitor imonitor,
+			final ProgressMonitor wmonitor) {
 		this.db = repo;
-		this.monitor = monitor;
+		initMonitor = imonitor;
+		writeMonitor = wmonitor;
 		this.deflater = new Deflater(db.getConfig().getCore().getCompression());
 	}
 
@@ -447,6 +472,17 @@ public void preparePack(
 	}
 
 	/**
+	 * Determine if the pack file will contain the requested object.
+	 * 
+	 * @param id
+	 *            the object to test the existence of.
+	 * @return true if the object will appear in the output pack file.
+	 */
+	public boolean willInclude(final AnyObjectId id) {
+		return objectsMap.get(id) != null;
+	}
+
+	/**
 	 * Computes SHA-1 of lexicographically sorted objects ids written in this
 	 * pack, as used to name a pack file in repository.
 	 *
@@ -529,23 +565,23 @@ public void writePack(OutputStream packStream) throws IOException {
 		countingOut = new CountingOutputStream(packStream);
 		out = new DigestOutputStream(countingOut, Constants.newMessageDigest());
 
-		monitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());
+		writeMonitor.beginTask(WRITING_OBJECTS_PROGRESS, getObjectsNumber());
 		writeHeader();
 		writeObjects();
 		writeChecksum();
 
 		out.flush();
 		windowCursor.release();
-		monitor.endTask();
+		writeMonitor.endTask();
 	}
 
 	private void searchForReuse() throws IOException {
-		monitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
+		initMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
 		final Collection<PackedObjectLoader> reuseLoaders = new LinkedList<PackedObjectLoader>();
 
 		for (List<ObjectToPack> list : objectsLists) {
 			for (ObjectToPack otp : list) {
-				if (monitor.isCancelled())
+				if (initMonitor.isCancelled())
 					throw new IOException(
 							"Packing cancelled during objects writing");
 				reuseLoaders.clear();
@@ -557,11 +593,11 @@ private void searchForReuse() throws IOException {
 				if (reuseObjects && !otp.hasReuseLoader()) {
 					selectObjectReuseForObject(otp, reuseLoaders);
 				}
-				monitor.update(1);
+				initMonitor.update(1);
 			}
 		}
 
-		monitor.endTask();
+		initMonitor.endTask();
 	}
 
 	private void selectDeltaReuseForObject(final ObjectToPack otp,
@@ -625,7 +661,7 @@ private void writeHeader() throws IOException {
 	private void writeObjects() throws IOException {
 		for (List<ObjectToPack> list : objectsLists) {
 			for (ObjectToPack otp : list) {
-				if (monitor.isCancelled())
+				if (writeMonitor.isCancelled())
 					throw new IOException(
 							"Packing cancelled during objects writing");
 				if (!otp.isWritten())
@@ -663,7 +699,7 @@ private void writeObject(final ObjectToPack otp) throws IOException {
 		else
 			writeWholeObject(otp);
 
-		monitor.update(1);
+		writeMonitor.update(1);
 	}
 
 	private void writeWholeObject(final ObjectToPack otp) throws IOException {
@@ -760,21 +796,34 @@ private ObjectWalk setUpWalker(
 	private void findObjectsToPack(final ObjectWalk walker)
 			throws MissingObjectException, IncorrectObjectTypeException,
 			IOException {
-		monitor.beginTask(COUNTING_OBJECTS_PROGRESS, ProgressMonitor.UNKNOWN);
+		initMonitor.beginTask(COUNTING_OBJECTS_PROGRESS,
+				ProgressMonitor.UNKNOWN);
 		RevObject o;
 
 		while ((o = walker.next()) != null) {
 			addObject(o);
-			monitor.update(1);
+			initMonitor.update(1);
 		}
 		while ((o = walker.nextObject()) != null) {
 			addObject(o);
-			monitor.update(1);
+			initMonitor.update(1);
 		}
-		monitor.endTask();
+		initMonitor.endTask();
 	}
 
-	private void addObject(RevObject object)
+	/**
+	 * Include one object to the output file.
+	 * <p>
+	 * Objects are written in the order they are added. If the same object is
+	 * added twice, it may be written twice, creating a larger than necessary
+	 * file.
+	 * 
+	 * @param object
+	 *            the object to add.
+	 * @throws IncorrectObjectTypeException
+	 *             the object is an unsupported type.
+	 */
+	public void addObject(final RevObject object)
 			throws IncorrectObjectTypeException {
 		if (object.has(RevFlag.UNINTERESTING)) {
 			edgeObjects.add(object);
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
index 2cb9b64..1fe504b 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackFetchConnection.java
@@ -100,6 +100,8 @@
 
 	static final String OPTION_SHALLOW = "shallow";
 
+	static final String OPTION_NO_PROGRESS = "no-progress";
+
 	private final RevWalk walk;
 
 	/** All commits that are immediately reachable by a local ref. */
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
index c225740..b5097ef 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/Daemon.java
@@ -41,6 +41,7 @@
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InterruptedIOException;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
@@ -96,15 +97,32 @@ public Daemon(final InetSocketAddress addr) {
 		exportBase = new ArrayList<File>();
 		processors = new ThreadGroup("Git-Daemon");
 
-		services = new DaemonService[] { new DaemonService("receive-pack",
-				"receivepack") {
-			@Override
-			protected void execute(final DaemonClient dc, final Repository db)
-					throws IOException {
-				final ReceivePack rp = new ReceivePack(db);
-				rp.receive(dc.getInputStream(), dc.getOutputStream(), null);
-			}
-		} };
+		services = new DaemonService[] {
+				new DaemonService("upload-pack", "uploadpack") {
+					{
+						setEnabled(true);
+					}
+
+					@Override
+					protected void execute(final DaemonClient dc,
+							final Repository db) throws IOException {
+						final UploadPack rp = new UploadPack(db);
+						final InputStream in = dc.getInputStream();
+						rp.upload(in, dc.getOutputStream(), null);
+					}
+				}, new DaemonService("receive-pack", "receivepack") {
+					{
+						setEnabled(false);
+					}
+
+					@Override
+					protected void execute(final DaemonClient dc,
+							final Repository db) throws IOException {
+						final ReceivePack rp = new ReceivePack(db);
+						final InputStream in = dc.getInputStream();
+						rp.receive(in, dc.getOutputStream(), null);
+					}
+				} };
 	}
 
 	/** @return the address connections are received on. */
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java
index d37b217..aae4be5 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineOut.java
@@ -50,7 +50,7 @@
 
 	PacketLineOut(final OutputStream i) {
 		out = i;
-		lenbuffer = new byte[4];
+		lenbuffer = new byte[5];
 	}
 
 	void writeString(final String s) throws IOException {
@@ -58,12 +58,22 @@ void writeString(final String s) throws IOException {
 	}
 
 	void writePacket(final byte[] packet) throws IOException {
-		writeLength(packet.length + 4);
+		formatLength(packet.length + 4);
+		out.write(lenbuffer, 0, 4);
 		out.write(packet);
 	}
 
+	void writeChannelPacket(final int channel, final byte[] buf, int off,
+			int len) throws IOException {
+		formatLength(len + 5);
+		lenbuffer[4] = (byte) channel;
+		out.write(lenbuffer, 0, 5);
+		out.write(buf, off, len);
+	}
+
 	void end() throws IOException {
-		writeLength(0);
+		formatLength(0);
+		out.write(lenbuffer, 0, 4);
 		flush();
 	}
 
@@ -74,7 +84,7 @@ void flush() throws IOException {
 	private static final byte[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
 			'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
 
-	private void writeLength(int w) throws IOException {
+	private void formatLength(int w) {
 		int o = 3;
 		while (o >= 0 && w != 0) {
 			lenbuffer[o--] = hexchar[w & 0xf];
@@ -82,6 +92,5 @@ private void writeLength(int w) throws IOException {
 		}
 		while (o >= 0)
 			lenbuffer[o--] = '0';
-		out.write(lenbuffer, 0, 4);
 	}
 }
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java
index 3ec9bff..b08cf4d 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java
@@ -66,11 +66,11 @@
  * @see PacketLineIn#sideband(ProgressMonitor)
  */
 class SideBandInputStream extends InputStream {
-	private static final int CH_DATA = 1;
+	static final int CH_DATA = 1;
 
-	private static final int CH_PROGRESS = 2;
+	static final int CH_PROGRESS = 2;
 
-	private static final int CH_ERROR = 3;
+	static final int CH_ERROR = 3;
 
 	private static Pattern P_UNBOUNDED = Pattern.compile(
 			".*?([\\w ]+): (\\d+)(, done)?.*", Pattern.DOTALL);
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java
new file mode 100644
index 0000000..31c9f5d
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Multiplexes data and progress messages
+ * <p>
+ * To correctly use this class you must wrap it in a BufferedOutputStream with a
+ * buffer size no larger than either {@link #SMALL_BUF} or {@link #MAX_BUF},
+ * minus {@link #HDR_SIZE}.
+ */
+class SideBandOutputStream extends OutputStream {
+	static final int CH_DATA = SideBandInputStream.CH_DATA;
+
+	static final int CH_PROGRESS = SideBandInputStream.CH_PROGRESS;
+
+	static final int CH_ERROR = SideBandInputStream.CH_ERROR;
+
+	static final int SMALL_BUF = 1000;
+
+	static final int MAX_BUF = 65520;
+
+	static final int HDR_SIZE = 5;
+
+	private final int channel;
+
+	private final PacketLineOut pckOut;
+
+	private byte[] singleByteBuffer;
+
+	SideBandOutputStream(final int chan, final PacketLineOut out) {
+		channel = chan;
+		pckOut = out;
+	}
+
+	@Override
+	public void flush() throws IOException {
+		if (channel != CH_DATA)
+			pckOut.flush();
+	}
+
+	@Override
+	public void write(final byte[] b, final int off, final int len)
+			throws IOException {
+		pckOut.writeChannelPacket(channel, b, off, len);
+	}
+
+	@Override
+	public void write(final int b) throws IOException {
+		if (singleByteBuffer == null)
+			singleByteBuffer = new byte[1];
+		singleByteBuffer[0] = (byte) b;
+		write(singleByteBuffer);
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java
new file mode 100644
index 0000000..5cda7c5
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.BufferedOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.ProgressMonitor;
+
+/** Write progress messages out to the sideband channel. */
+class SideBandProgressMonitor implements ProgressMonitor {
+	private PrintWriter out;
+
+	private boolean output;
+
+	private long taskBeganAt;
+
+	private long lastOutput;
+
+	private String msg;
+
+	private int lastWorked;
+
+	private int totalWork;
+
+	SideBandProgressMonitor(final PacketLineOut pckOut) {
+		final int bufsz = SideBandOutputStream.SMALL_BUF
+				- SideBandOutputStream.HDR_SIZE;
+		out = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(
+				new SideBandOutputStream(SideBandOutputStream.CH_PROGRESS,
+						pckOut), bufsz), Constants.CHARSET));
+	}
+
+	public void start(final int totalTasks) {
+		// Ignore the number of tasks.
+		taskBeganAt = System.currentTimeMillis();
+		lastOutput = taskBeganAt;
+	}
+
+	public void beginTask(final String title, final int total) {
+		endTask();
+		msg = title;
+		lastWorked = 0;
+		totalWork = total;
+	}
+
+	public void update(final int completed) {
+		if (msg == null)
+			return;
+
+		final int cmp = lastWorked + completed;
+		final long now = System.currentTimeMillis();
+		if (!output && now - taskBeganAt < 500)
+			return;
+		if (totalWork == UNKNOWN) {
+			if (now - lastOutput >= 500) {
+				display(cmp, null);
+				lastOutput = now;
+			}
+		} else {
+			if ((cmp * 100 / totalWork) != (lastWorked * 100) / totalWork
+					|| now - lastOutput >= 500) {
+				display(cmp, null);
+				lastOutput = now;
+			}
+		}
+		lastWorked = cmp;
+		output = true;
+	}
+
+	private void display(final int cmp, final String eol) {
+		final StringBuilder m = new StringBuilder();
+		m.append(msg);
+		m.append(": ");
+
+		if (totalWork == UNKNOWN) {
+			m.append(cmp);
+		} else {
+			final int pcnt = (cmp * 100 / totalWork);
+			if (pcnt < 100)
+				m.append(' ');
+			if (pcnt < 10)
+				m.append(' ');
+			m.append(pcnt);
+			m.append("% (");
+			m.append(cmp);
+			m.append("/");
+			m.append(totalWork);
+			m.append(")");
+		}
+		if (eol != null)
+			m.append(eol);
+		else
+			m.append("   \r");
+		out.print(m);
+		out.flush();
+	}
+
+	public boolean isCancelled() {
+		return false;
+	}
+
+	public void endTask() {
+		if (output) {
+			if (totalWork == UNKNOWN)
+				display(lastWorked, ", done\n");
+			else
+				display(totalWork, "\n");
+		}
+		output = false;
+		msg = null;
+	}
+}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java b/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java
new file mode 100644
index 0000000..4401951
--- /dev/null
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java
@@ -0,0 +1,491 @@
+/*
+ * Copyright (C) 2008, Google Inc.
+ *
+ * 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 Git Development Community 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.spearce.jgit.transport;
+
+import java.io.BufferedOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.spearce.jgit.errors.PackProtocolException;
+import org.spearce.jgit.lib.Constants;
+import org.spearce.jgit.lib.NullProgressMonitor;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.PackWriter;
+import org.spearce.jgit.lib.ProgressMonitor;
+import org.spearce.jgit.lib.Ref;
+import org.spearce.jgit.lib.RefComparator;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.revwalk.RevCommit;
+import org.spearce.jgit.revwalk.RevFlag;
+import org.spearce.jgit.revwalk.RevFlagSet;
+import org.spearce.jgit.revwalk.RevObject;
+import org.spearce.jgit.revwalk.RevTag;
+import org.spearce.jgit.revwalk.RevWalk;
+
+/**
+ * Implements the server side of a fetch connection, transmitting objects.
+ */
+public class UploadPack {
+	static final String OPTION_INCLUDE_TAG = BasePackFetchConnection.OPTION_INCLUDE_TAG;
+
+	static final String OPTION_MULTI_ACK = BasePackFetchConnection.OPTION_MULTI_ACK;
+
+	static final String OPTION_THIN_PACK = BasePackFetchConnection.OPTION_THIN_PACK;
+
+	static final String OPTION_SIDE_BAND = BasePackFetchConnection.OPTION_SIDE_BAND;
+
+	static final String OPTION_SIDE_BAND_64K = BasePackFetchConnection.OPTION_SIDE_BAND_64K;
+
+	static final String OPTION_OFS_DELTA = BasePackFetchConnection.OPTION_OFS_DELTA;
+
+	static final String OPTION_NO_PROGRESS = BasePackFetchConnection.OPTION_NO_PROGRESS;
+
+	/** Database we read the objects from. */
+	private final Repository db;
+
+	/** Revision traversal support over {@link #db}. */
+	private final RevWalk walk;
+
+	private InputStream rawIn;
+
+	private OutputStream rawOut;
+
+	private PacketLineIn pckIn;
+
+	private PacketLineOut pckOut;
+
+	/** The refs we advertised as existing at the start of the connection. */
+	private Map<String, Ref> refs;
+
+	/** Capabilities requested by the client. */
+	private final Set<String> options = new HashSet<String>();
+
+	/** Objects the client wants to obtain. */
+	private final List<RevObject> wantAll = new ArrayList<RevObject>();
+
+	/** Objects the client wants to obtain. */
+	private final List<RevCommit> wantCommits = new ArrayList<RevCommit>();
+
+	/** Objects on both sides, these don't have to be sent. */
+	private final List<RevObject> commonBase = new ArrayList<RevObject>();
+
+	/** Marked on objects we sent in our advertisement list. */
+	private final RevFlag ADVERTISED;
+
+	/** Marked on objects the client has asked us to give them. */
+	private final RevFlag WANT;
+
+	/** Marked on objects both we and the client have. */
+	private final RevFlag PEER_HAS;
+
+	/** Marked on objects in {@link #commonBase}. */
+	private final RevFlag COMMON;
+
+	private final RevFlagSet SAVE;
+
+	private boolean multiAck;
+
+	/**
+	 * Create a new pack upload for an open repository.
+	 * 
+	 * @param copyFrom
+	 *            the source repository.
+	 */
+	public UploadPack(final Repository copyFrom) {
+		db = copyFrom;
+		walk = new RevWalk(db);
+
+		ADVERTISED = walk.newFlag("ADVERTISED");
+		WANT = walk.newFlag("WANT");
+		PEER_HAS = walk.newFlag("PEER_HAS");
+		COMMON = walk.newFlag("COMMON");
+		walk.carry(PEER_HAS);
+
+		SAVE = new RevFlagSet();
+		SAVE.add(ADVERTISED);
+		SAVE.add(WANT);
+		SAVE.add(PEER_HAS);
+	}
+
+	/** @return the repository this receive completes into. */
+	public final Repository getRepository() {
+		return db;
+	}
+
+	/** @return the RevWalk instance used by this connection. */
+	public final RevWalk getRevWalk() {
+		return walk;
+	}
+
+	/**
+	 * Execute the upload task on the socket.
+	 * 
+	 * @param input
+	 *            raw input to read client commands from. Caller must ensure the
+	 *            input is buffered, otherwise read performance may suffer.
+	 * @param output
+	 *            response back to the Git network client, to write the pack
+	 *            data onto. Caller must ensure the output is buffered,
+	 *            otherwise write performance may suffer.
+	 * @param messages
+	 *            secondary "notice" channel to send additional messages out
+	 *            through. When run over SSH this should be tied back to the
+	 *            standard error channel of the command execution. For most
+	 *            other network connections this should be null.
+	 * @throws IOException
+	 */
+	public void upload(final InputStream input, final OutputStream output,
+			final OutputStream messages) throws IOException {
+		rawIn = input;
+		rawOut = output;
+
+		pckIn = new PacketLineIn(rawIn);
+		pckOut = new PacketLineOut(rawOut);
+		service();
+	}
+
+	private void service() throws IOException {
+		sendAdvertisedRefs();
+		recvWants();
+		if (wantAll.isEmpty())
+			return;
+		multiAck = options.contains(OPTION_MULTI_ACK);
+		negotiate();
+		sendPack();
+	}
+
+	private void sendAdvertisedRefs() throws IOException {
+		refs = db.getAllRefs();
+
+		final StringBuilder m = new StringBuilder(100);
+		final char[] idtmp = new char[2 * Constants.OBJECT_ID_LENGTH];
+		final Iterator<Ref> i = RefComparator.sort(refs.values()).iterator();
+		if (i.hasNext()) {
+			final Ref r = i.next();
+			final RevObject o = safeParseAny(r.getObjectId());
+			if (o != null) {
+				advertise(m, idtmp, o, r.getOrigName());
+				m.append('\0');
+				m.append(' ');
+				m.append(OPTION_INCLUDE_TAG);
+				m.append(' ');
+				m.append(OPTION_MULTI_ACK);
+				m.append(' ');
+				m.append(OPTION_OFS_DELTA);
+				m.append(' ');
+				m.append(OPTION_SIDE_BAND);
+				m.append(' ');
+				m.append(OPTION_SIDE_BAND_64K);
+				m.append(' ');
+				m.append(OPTION_THIN_PACK);
+				m.append(' ');
+				m.append(OPTION_NO_PROGRESS);
+				m.append(' ');
+				writeAdvertisedRef(m);
+				if (o instanceof RevTag)
+					writeAdvertisedTag(m, idtmp, o, r.getName());
+			}
+		}
+		while (i.hasNext()) {
+			final Ref r = i.next();
+			final RevObject o = safeParseAny(r.getObjectId());
+			if (o != null) {
+				advertise(m, idtmp, o, r.getOrigName());
+				writeAdvertisedRef(m);
+				if (o instanceof RevTag)
+					writeAdvertisedTag(m, idtmp, o, r.getName());
+			}
+		}
+		pckOut.end();
+	}
+
+	private RevObject safeParseAny(final ObjectId id) {
+		try {
+			return walk.parseAny(id);
+		} catch (IOException e) {
+			return null;
+		}
+	}
+
+	private void advertise(final StringBuilder m, final char[] idtmp,
+			final RevObject o, final String name) {
+		o.add(ADVERTISED);
+		m.setLength(0);
+		o.getId().copyTo(idtmp, m);
+		m.append(' ');
+		m.append(name);
+	}
+
+	private void writeAdvertisedRef(final StringBuilder m) throws IOException {
+		m.append('\n');
+		pckOut.writeString(m.toString());
+	}
+
+	private void writeAdvertisedTag(final StringBuilder m, final char[] idtmp,
+			final RevObject tag, final String name) throws IOException {
+		RevObject o = tag;
+		while (o instanceof RevTag) {
+			// Fully unwrap here so later on we have these already parsed.
+			try {
+				walk.parse(((RevTag) o).getObject());
+			} catch (IOException err) {
+				return;
+			}
+			o = ((RevTag) o).getObject();
+			o.add(ADVERTISED);
+		}
+		advertise(m, idtmp, ((RevTag) tag).getObject(), name + "^{}");
+		writeAdvertisedRef(m);
+	}
+
+	private void recvWants() throws IOException {
+		boolean isFirst = true;
+		for (;; isFirst = false) {
+			String line;
+			try {
+				line = pckIn.readString();
+			} catch (EOFException eof) {
+				if (isFirst)
+					break;
+				throw eof;
+			}
+
+			if (line.length() == 0)
+				break;
+			if (!line.startsWith("want ") || line.length() < 45)
+				throw new PackProtocolException("expected want; got " + line);
+
+			if (isFirst) {
+				final int sp = line.indexOf(' ', 45);
+				if (sp >= 0) {
+					for (String c : line.substring(sp + 1).split(" "))
+						options.add(c);
+					line = line.substring(0, sp);
+				}
+			}
+
+			final ObjectId id = ObjectId.fromString(line.substring(5));
+			final RevObject o;
+			try {
+				o = walk.parseAny(id);
+			} catch (IOException e) {
+				throw new PackProtocolException(id.name() + " not valid", e);
+			}
+			if (!o.has(ADVERTISED))
+				throw new PackProtocolException(id.name() + " not valid");
+			want(o);
+		}
+	}
+
+	private void want(RevObject o) {
+		if (!o.has(WANT)) {
+			o.add(WANT);
+			wantAll.add(o);
+
+			if (o instanceof RevCommit)
+				wantCommits.add((RevCommit) o);
+
+			else if (o instanceof RevTag) {
+				do {
+					o = ((RevTag) o).getObject();
+				} while (o instanceof RevTag);
+				if (o instanceof RevCommit)
+					want(o);
+			}
+		}
+	}
+
+	private void negotiate() throws IOException {
+		ObjectId last = ObjectId.zeroId();
+		for (;;) {
+			String line;
+			try {
+				line = pckIn.readString();
+			} catch (EOFException eof) {
+				throw eof;
+			}
+
+			if (line.length() == 0) {
+				if (commonBase.isEmpty() || multiAck)
+					pckOut.writeString("NAK\n");
+
+			} else if (line.startsWith("have ") && line.length() == 45) {
+				final ObjectId id = ObjectId.fromString(line.substring(5));
+				if (matchHave(id)) {
+					// Both sides have the same object; let the client know.
+					//
+					if (multiAck) {
+						last = id;
+						pckOut.writeString("ACK " + id.name() + " continue\n");
+					} else if (commonBase.size() == 1)
+						pckOut.writeString("ACK " + id.name() + "\n");
+				} else {
+					// They have this object; we don't.
+					//
+					if (multiAck && okToGiveUp())
+						pckOut.writeString("ACK " + id.name() + " continue\n");
+				}
+
+			} else if (line.equals("done")) {
+				if (commonBase.isEmpty())
+					pckOut.writeString("NAK\n");
+
+				else if (multiAck)
+					pckOut.writeString("ACK " + last.name() + "\n");
+				break;
+
+			} else {
+				throw new PackProtocolException("expected have; got " + line);
+			}
+		}
+	}
+
+	private boolean matchHave(final ObjectId id) {
+		final RevObject o;
+		try {
+			o = walk.parseAny(id);
+		} catch (IOException err) {
+			return false;
+		}
+
+		if (!o.has(PEER_HAS)) {
+			o.add(PEER_HAS);
+			if (o instanceof RevCommit)
+				((RevCommit) o).carry(PEER_HAS);
+			if (!o.has(COMMON)) {
+				o.add(COMMON);
+				commonBase.add(o);
+			}
+		}
+		return true;
+	}
+
+	private boolean okToGiveUp() throws PackProtocolException {
+		if (commonBase.isEmpty())
+			return false;
+
+		try {
+			for (final Iterator<RevCommit> i = wantCommits.iterator(); i
+					.hasNext();) {
+				final RevCommit want = i.next();
+				if (wantSatisfied(want))
+					i.remove();
+			}
+		} catch (IOException e) {
+			throw new PackProtocolException("internal revision error", e);
+		}
+		return wantCommits.isEmpty();
+	}
+
+	private boolean wantSatisfied(final RevCommit want) throws IOException {
+		walk.resetRetain(SAVE);
+		walk.markStart(want);
+		for (;;) {
+			final RevCommit c = walk.next();
+			if (c == null)
+				break;
+			if (c.has(PEER_HAS)) {
+				if (!c.has(COMMON)) {
+					c.add(COMMON);
+					commonBase.add(c);
+				}
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private void sendPack() throws IOException {
+		final boolean thin = options.contains(OPTION_THIN_PACK);
+		final boolean progress = !options.contains(OPTION_NO_PROGRESS);
+		final boolean sideband = options.contains(OPTION_SIDE_BAND)
+				|| options.contains(OPTION_SIDE_BAND_64K);
+
+		ProgressMonitor pm = NullProgressMonitor.INSTANCE;
+		OutputStream packOut = rawOut;
+
+		if (sideband) {
+			int bufsz = SideBandOutputStream.SMALL_BUF;
+			if (options.contains(OPTION_SIDE_BAND_64K))
+				bufsz = SideBandOutputStream.MAX_BUF;
+			bufsz -= SideBandOutputStream.HDR_SIZE;
+
+			packOut = new BufferedOutputStream(new SideBandOutputStream(
+					SideBandOutputStream.CH_DATA, pckOut), bufsz);
+
+			if (progress)
+				pm = new SideBandProgressMonitor(pckOut);
+		}
+
+		final PackWriter pw;
+		pw = new PackWriter(db, pm, NullProgressMonitor.INSTANCE);
+		pw.setDeltaBaseAsOffset(options.contains(OPTION_OFS_DELTA));
+		pw.preparePack(wantAll, commonBase, thin, true);
+		if (options.contains(OPTION_INCLUDE_TAG)) {
+			for (final Ref r : refs.values()) {
+				final RevObject o;
+				try {
+					o = walk.parseAny(r.getObjectId());
+				} catch (IOException e) {
+					continue;
+				}
+				if (o.has(WANT) || !(o instanceof RevTag))
+					continue;
+				final RevTag t = (RevTag) o;
+				if (!pw.willInclude(t) && pw.willInclude(t.getObject()))
+					pw.addObject(t);
+			}
+		}
+		pw.writePack(packOut);
+
+		if (sideband) {
+			packOut.flush();
+			pckOut.end();
+		} else {
+			rawOut.flush();
+		}
+	}
+}
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 4/5] Fix BaseFetchPackConnection's output of selected capabilities
From: Shawn O. Pearce @ 2008-12-23 22:56 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230073007-17337-4-git-send-email-spearce@spearce.org>

When we output the first "have ..." line for an upload-pack server
process we need to dump "have $id $cap1 $cap2 ..", where there is
a space between the SHA-1 $id and the first capability name $cap1.
If we don't dump that space we run into errors with our own version
of upload-pack not being able to parse the SHA-1 out of the line,
as the line was split incorrectly.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../spearce/jgit/transport/BasePackConnection.java |    3 +--
 .../jgit/transport/BasePackPushConnection.java     |    2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
index e9df30e..c9232ce 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackConnection.java
@@ -207,8 +207,7 @@ protected boolean isCapableOf(final String option) {
 	protected boolean wantCapability(final StringBuilder b, final String option) {
 		if (!isCapableOf(option))
 			return false;
-		if (b.length() > 0)
-			b.append(' ');
+		b.append(' ');
 		b.append(option);
 		return true;
 	}
diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
index 17f6915..a078d7e 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/BasePackPushConnection.java
@@ -181,7 +181,7 @@ private String enableCapabilities() {
 		capableReport = wantCapability(line, CAPABILITY_REPORT_STATUS);
 		capableDeleteRefs = wantCapability(line, CAPABILITY_DELETE_REFS);
 		if (line.length() > 0)
-			line.insert(0, '\0');
+			line.setCharAt(0, '\0');
 		return line.toString();
 	}
 
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 2/5] Permit subclass of ObjectId (e.g. RevObject) when calling PackWriter
From: Shawn O. Pearce @ 2008-12-23 22:56 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230073007-17337-2-git-send-email-spearce@spearce.org>

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/lib/PackWriter.java       |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
index 32bf738..32394f2 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/PackWriter.java
@@ -436,8 +436,9 @@ public void preparePack(final Iterator<RevObject> objectsSource)
 	 * @throws IOException
 	 *             when some I/O problem occur during reading objects.
 	 */
-	public void preparePack(final Collection<ObjectId> interestingObjects,
-			final Collection<ObjectId> uninterestingObjects,
+	public void preparePack(
+			final Collection<? extends ObjectId> interestingObjects,
+			final Collection<? extends ObjectId> uninterestingObjects,
 			final boolean thin, final boolean ignoreMissingUninteresting)
 			throws IOException {
 		ObjectWalk walker = setUpWalker(interestingObjects,
@@ -727,8 +728,8 @@ private void writeChecksum() throws IOException {
 	}
 
 	private ObjectWalk setUpWalker(
-			final Collection<ObjectId> interestingObjects,
-			final Collection<ObjectId> uninterestingObjects,
+			final Collection<? extends ObjectId> interestingObjects,
+			final Collection<? extends ObjectId> uninterestingObjects,
 			final boolean thin, final boolean ignoreMissingUninteresting)
 			throws MissingObjectException, IOException,
 			IncorrectObjectTypeException {
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 1/5] Sort Ref objects by OrigName and not Name
From: Shawn O. Pearce @ 2008-12-23 22:56 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git
In-Reply-To: <1230073007-17337-1-git-send-email-spearce@spearce.org>

This avoids sorting symrefs by their target; instead we sort the
symref by the symref's own name, thus placing "HEAD" before the
standard "refs/heads/..." namespace.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../src/org/spearce/jgit/lib/RefComparator.java    |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java
index 95e3e0f..940a7ec 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefComparator.java
@@ -54,7 +54,7 @@
 	public static final RefComparator INSTANCE = new RefComparator();
 
 	public int compare(final Ref o1, final Ref o2) {
-		return o1.getName().compareTo(o2.getName());
+		return o1.getOrigName().compareTo(o2.getOrigName());
 	}
 
 	/**
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* [JGIT PATCH 0/5] Add "jgit upload-pack" for fetch service
From: Shawn O. Pearce @ 2008-12-23 22:56 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

And uh, now we can act as a true git server, providing fetch and
clone support over git:// with our own daemon, or through the use of
"git fetch --upload-pack='jgit upload-pack'".

Roughly tested by cloning the Linux kernel and WebKit (both about
500 MB packed).  Kernel takes a while to enumerate the objects,
but eh, it works.  :-)


Shawn O. Pearce (5):
  Sort Ref objects by OrigName and not Name
  Permit subclass of ObjectId (e.g. RevObject) when calling PackWriter
  Implement "jgit upload-pack" to support fetching from jgit
  Fix BaseFetchPackConnection's output of selected capabilities
  Switch local fetch connection to use our own UploadPack

 .../services/org.spearce.jgit.pgm.TextBuiltin      |    1 +
 .../src/org/spearce/jgit/pgm/UploadPack.java       |   67 +++
 .../src/org/spearce/jgit/lib/PackWriter.java       |   94 +++-
 .../src/org/spearce/jgit/lib/RefComparator.java    |    2 +-
 .../spearce/jgit/transport/BasePackConnection.java |    3 +-
 .../jgit/transport/BasePackFetchConnection.java    |    4 +
 .../jgit/transport/BasePackPushConnection.java     |    2 +-
 .../src/org/spearce/jgit/transport/Daemon.java     |   36 ++-
 .../org/spearce/jgit/transport/PacketLineOut.java  |   19 +-
 .../jgit/transport/SideBandInputStream.java        |    6 +-
 .../jgit/transport/SideBandOutputStream.java       |   93 ++++
 .../jgit/transport/SideBandProgressMonitor.java    |  150 ++++++
 .../org/spearce/jgit/transport/TransportLocal.java |   96 ++++-
 .../src/org/spearce/jgit/transport/UploadPack.java |  491 ++++++++++++++++++++
 14 files changed, 1018 insertions(+), 46 deletions(-)
 create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/UploadPack.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandOutputStream.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/SideBandProgressMonitor.java
 create mode 100644 org.spearce.jgit/src/org/spearce/jgit/transport/UploadPack.java

^ permalink raw reply

* [JGIT PATCH] Teach TransportLocal how to use our own ReceivePack implementation
From: Shawn O. Pearce @ 2008-12-23 18:34 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git

If the user hasn't reconfigured git receive-pack on this remote
then we can use our internal implementation instead of forking
out to the C implementation.  It may be faster to stay within
the JVM then to start the external process.

In-memory pipes are constructed to copy the data between the
calling client thread and the background ReceivePack worker.
In certain cases we may even be able to do a faster copy by
directly hard-linking object files and/or packs, but that
isn't traditionally how Git behaves, so we use the standard
network protocol anyway.

Since our ReceivePack process doesn't fire hooks by default
we no longer will invoke any hooks during a push to a local
repository.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 .../org/spearce/jgit/transport/TransportLocal.java |   88 +++++++++++++++++++-
 1 files changed, 85 insertions(+), 3 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
index d74f1b3..b5dd5fc 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportLocal.java
@@ -43,6 +43,8 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
 
 import org.spearce.jgit.errors.NotSupportedException;
 import org.spearce.jgit.errors.TransportException;
@@ -90,7 +92,10 @@ public FetchConnection openFetch() throws TransportException {
 	@Override
 	public PushConnection openPush() throws NotSupportedException,
 			TransportException {
-		return new LocalPushConnection();
+		final String rp = getOptionReceivePack();
+		if ("git-receive-pack".equals(rp) || "git receive-pack".equals(rp))
+			return new InternalLocalPushConnection();
+		return new ForkLocalPushConnection();
 	}
 
 	@Override
@@ -151,10 +156,87 @@ public void close() {
 		}
 	}
 
-	class LocalPushConnection extends BasePackPushConnection {
+	class InternalLocalPushConnection extends BasePackPushConnection {
+		private Thread worker;
+
+		InternalLocalPushConnection() throws TransportException {
+			super(TransportLocal.this);
+
+			final Repository dst;
+			try {
+				dst = new Repository(remoteGitDir);
+			} catch (IOException err) {
+				throw new TransportException(uri, "not a git directory");
+			}
+
+			final PipedInputStream in_r;
+			final PipedOutputStream in_w;
+
+			final PipedInputStream out_r;
+			final PipedOutputStream out_w;
+			try {
+				in_r = new PipedInputStream();
+				in_w = new PipedOutputStream(in_r);
+
+				out_r = new PipedInputStream();
+				out_w = new PipedOutputStream(out_r);
+			} catch (IOException err) {
+				dst.close();
+				throw new TransportException(uri, "cannot connect pipes", err);
+			}
+
+			worker = new Thread("JGit-Receive-Pack") {
+				public void run() {
+					try {
+						final ReceivePack rp = new ReceivePack(dst);
+						rp.receive(out_r, in_w, System.err);
+					} catch (IOException err) {
+						// Client side of the pipes should report the problem.
+					} catch (RuntimeException err) {
+						// Clients side will notice we went away, and report.
+					} finally {
+						try {
+							out_r.close();
+						} catch (IOException e2) {
+							// Ignore close failure, we probably crashed above.
+						}
+
+						try {
+							in_w.close();
+						} catch (IOException e2) {
+							// Ignore close failure, we probably crashed above.
+						}
+
+						dst.close();
+					}
+				}
+			};
+			worker.start();
+
+			init(in_r, out_w);
+			readAdvertisedRefs();
+		}
+
+		@Override
+		public void close() {
+			super.close();
+
+			if (worker != null) {
+				try {
+					worker.join();
+				} catch (InterruptedException ie) {
+					// Stop waiting and return anyway.
+				} finally {
+					worker = null;
+				}
+			}
+		}
+	}
+
+	class ForkLocalPushConnection extends BasePackPushConnection {
 		private Process receivePack;
 
-		LocalPushConnection() throws TransportException {
+		ForkLocalPushConnection() throws TransportException {
 			super(TransportLocal.this);
 			receivePack = startProcessWithErrStream(getOptionReceivePack());
 			init(receivePack.getInputStream(), receivePack.getOutputStream());
-- 
1.6.1.rc4.301.g5497a

^ permalink raw reply related

* Re: 'Theirs' merge between branches on a binary file.
From: René Scharfe @ 2008-12-23 18:31 UTC (permalink / raw)
  To: Tim Visher; +Cc: Junio C Hamano, git
In-Reply-To: <c115fd3c0812230605x369af9c0n372db761fa11ce39@mail.gmail.com>

Tim Visher schrieb:
> I'm now working on
> compiling git under cygwin as the latest version cygwin installs for
> you is 0.4!

Have you seen msysgit (http://code.google.com/p/msysgit/), the easy
route to git on Windows?  It has all you need to check out and compile
the latest version of git.

René

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox