diff --git a/compat/nedmalloc/nedmalloc.c b/compat/nedmalloc/nedmalloc.c
index d9a17a8057c574..875b792215d2ff 100644
--- a/compat/nedmalloc/nedmalloc.c
+++ b/compat/nedmalloc/nedmalloc.c
@@ -954,8 +954,9 @@ char *strdup(const char *s1)
 {
 	char *s2 = 0;
 	if (s1) {
-		s2 = malloc(strlen(s1) + 1);
-		strcpy(s2, s1);
+		size_t len = strlen(s1) + 1;
+		s2 = malloc(len);
+		memcpy(s2, s1, len);
 	}
 	return s2;
 }
diff --git a/compat/nedmalloc/nedmalloc.c.orig b/compat/nedmalloc/nedmalloc.c.orig
new file mode 100644
index 00000000000000..d9a17a8057c574
--- /dev/null
+++ b/compat/nedmalloc/nedmalloc.c.orig
@@ -0,0 +1,966 @@
+/* Alternative malloc implementation for multiple threads without
+lock contention based on dlmalloc. (C) 2005-2006 Niall Douglas
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef _MSC_VER
+/* Enable full aliasing on MSVC */
+/*#pragma optimize("a", on)*/
+#endif
+
+/*#define FULLSANITYCHECKS*/
+
+#include "nedmalloc.h"
+#if defined(WIN32)
+ #include <malloc.h>
+#endif
+#define MSPACES 1
+#define ONLY_MSPACES 1
+#ifndef USE_LOCKS
+ #define USE_LOCKS 1
+#endif
+#define FOOTERS 1           /* Need to enable footers so frees lock the right mspace */
+#undef DEBUG				/* dlmalloc wants DEBUG either 0 or 1 */
+#ifdef _DEBUG
+ #define DEBUG 1
+#else
+ #define DEBUG 0
+#endif
+#ifdef NDEBUG               /* Disable assert checking on release builds */
+ #undef DEBUG
+#endif
+/* The default of 64Kb means we spend too much time kernel-side */
+#ifndef DEFAULT_GRANULARITY
+#define DEFAULT_GRANULARITY (1*1024*1024)
+#endif
+/*#define USE_SPIN_LOCKS 0*/
+
+
+/*#define FORCEINLINE*/
+#include "malloc.c.h"
+#ifdef NDEBUG               /* Disable assert checking on release builds */
+ #undef DEBUG
+#endif
+
+/* The maximum concurrent threads in a pool possible */
+#ifndef MAXTHREADSINPOOL
+#define MAXTHREADSINPOOL 16
+#endif
+/* The maximum number of threadcaches which can be allocated */
+#ifndef THREADCACHEMAXCACHES
+#define THREADCACHEMAXCACHES 256
+#endif
+/* The maximum size to be allocated from the thread cache */
+#ifndef THREADCACHEMAX
+#define THREADCACHEMAX 8192
+#endif
+#if 0
+/* The number of cache entries for finer grained bins. This is (topbitpos(THREADCACHEMAX)-4)*2 */
+#define THREADCACHEMAXBINS ((13-4)*2)
+#else
+/* The number of cache entries. This is (topbitpos(THREADCACHEMAX)-4) */
+#define THREADCACHEMAXBINS (13-4)
+#endif
+/* Point at which the free space in a thread cache is garbage collected */
+#ifndef THREADCACHEMAXFREESPACE
+#define THREADCACHEMAXFREESPACE (512*1024)
+#endif
+
+
+#ifdef WIN32
+ #define TLSVAR			DWORD
+ #define TLSALLOC(k)	(*(k)=TlsAlloc(), TLS_OUT_OF_INDEXES==*(k))
+ #define TLSFREE(k)		(!TlsFree(k))
+ #define TLSGET(k)		TlsGetValue(k)
+ #define TLSSET(k, a)	(!TlsSetValue(k, a))
+ #ifdef DEBUG
+static LPVOID ChkedTlsGetValue(DWORD idx)
+{
+	LPVOID ret=TlsGetValue(idx);
+	assert(S_OK==GetLastError());
+	return ret;
+}
+  #undef TLSGET
+  #define TLSGET(k) ChkedTlsGetValue(k)
+ #endif
+#else
+ #define TLSVAR			pthread_key_t
+ #define TLSALLOC(k)	pthread_key_create(k, 0)
+ #define TLSFREE(k)		pthread_key_delete(k)
+ #define TLSGET(k)		pthread_getspecific(k)
+ #define TLSSET(k, a)	pthread_setspecific(k, a)
+#endif
+
+#if 0
+/* Only enable if testing with valgrind. Causes misoperation */
+#define mspace_malloc(p, s) malloc(s)
+#define mspace_realloc(p, m, s) realloc(m, s)
+#define mspace_calloc(p, n, s) calloc(n, s)
+#define mspace_free(p, m) free(m)
+#endif
+
+
+#if defined(__cplusplus)
+#if !defined(NO_NED_NAMESPACE)
+namespace nedalloc {
+#else
+extern "C" {
+#endif
+#endif
+
+size_t nedblksize(void *mem) THROWSPEC
+{
+#if 0
+	/* Only enable if testing with valgrind. Causes misoperation */
+	return THREADCACHEMAX;
+#else
+	if(mem)
+	{
+		mchunkptr p=mem2chunk(mem);
+		assert(cinuse(p));	/* If this fails, someone tried to free a block twice */
+		if(cinuse(p))
+			return chunksize(p)-overhead_for(p);
+	}
+	return 0;
+#endif
+}
+
+void nedsetvalue(void *v) THROWSPEC					{ nedpsetvalue(0, v); }
+void * nedmalloc(size_t size) THROWSPEC				{ return nedpmalloc(0, size); }
+void * nedcalloc(size_t no, size_t size) THROWSPEC	{ return nedpcalloc(0, no, size); }
+void * nedrealloc(void *mem, size_t size) THROWSPEC	{ return nedprealloc(0, mem, size); }
+void   nedfree(void *mem) THROWSPEC					{ nedpfree(0, mem); }
+void * nedmemalign(size_t alignment, size_t bytes) THROWSPEC { return nedpmemalign(0, alignment, bytes); }
+#if !NO_MALLINFO
+struct mallinfo nedmallinfo(void) THROWSPEC			{ return nedpmallinfo(0); }
+#endif
+int    nedmallopt(int parno, int value) THROWSPEC	{ return nedpmallopt(0, parno, value); }
+int    nedmalloc_trim(size_t pad) THROWSPEC			{ return nedpmalloc_trim(0, pad); }
+void   nedmalloc_stats() THROWSPEC					{ nedpmalloc_stats(0); }
+size_t nedmalloc_footprint() THROWSPEC				{ return nedpmalloc_footprint(0); }
+void **nedindependent_calloc(size_t elemsno, size_t elemsize, void **chunks) THROWSPEC	{ return nedpindependent_calloc(0, elemsno, elemsize, chunks); }
+void **nedindependent_comalloc(size_t elems, size_t *sizes, void **chunks) THROWSPEC	{ return nedpindependent_comalloc(0, elems, sizes, chunks); }
+
+struct threadcacheblk_t;
+typedef struct threadcacheblk_t threadcacheblk;
+struct threadcacheblk_t
+{	/* Keep less than 16 bytes on 32 bit systems and 32 bytes on 64 bit systems */
+#ifdef FULLSANITYCHECKS
+	unsigned int magic;
+#endif
+	unsigned int lastUsed, size;
+	threadcacheblk *next, *prev;
+};
+typedef struct threadcache_t
+{
+#ifdef FULLSANITYCHECKS
+	unsigned int magic1;
+#endif
+	int mymspace;						/* Last mspace entry this thread used */
+	long threadid;
+	unsigned int mallocs, frees, successes;
+	size_t freeInCache;					/* How much free space is stored in this cache */
+	threadcacheblk *bins[(THREADCACHEMAXBINS+1)*2];
+#ifdef FULLSANITYCHECKS
+	unsigned int magic2;
+#endif
+} threadcache;
+struct nedpool_t
+{
+	MLOCK_T mutex;
+	void *uservalue;
+	int threads;						/* Max entries in m to use */
+	threadcache *caches[THREADCACHEMAXCACHES];
+	TLSVAR mycache;						/* Thread cache for this thread. 0 for unset, negative for use mspace-1 directly, otherwise is cache-1 */
+	mstate m[MAXTHREADSINPOOL+1];		/* mspace entries for this pool */
+};
+static nedpool syspool;
+
+static FORCEINLINE unsigned int size2binidx(size_t _size) THROWSPEC
+{	/* 8=1000	16=10000	20=10100	24=11000	32=100000	48=110000	4096=1000000000000 */
+	unsigned int topbit, size=(unsigned int)(_size>>4);
+	/* 16=1		20=1	24=1	32=10	48=11	64=100	96=110	128=1000	4096=100000000 */
+
+#if defined(__GNUC__)
+	topbit = sizeof(size)*__CHAR_BIT__ - 1 - __builtin_clz(size);
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+	{
+	    unsigned long bsrTopBit;
+
+	    _BitScanReverse(&bsrTopBit, size);
+
+	    topbit = bsrTopBit;
+	}
+#else
+#if 0
+	union {
+		unsigned asInt[2];
+		double asDouble;
+	};
+	int n;
+
+	asDouble = (double)size + 0.5;
+	topbit = (asInt[!FOX_BIGENDIAN] >> 20) - 1023;
+#else
+	{
+		unsigned int x=size;
+		x = x | (x >> 1);
+		x = x | (x >> 2);
+		x = x | (x >> 4);
+		x = x | (x >> 8);
+		x = x | (x >>16);
+		x = ~x;
+		x = x - ((x >> 1) & 0x55555555);
+		x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+		x = (x + (x >> 4)) & 0x0F0F0F0F;
+		x = x + (x << 8);
+		x = x + (x << 16);
+		topbit=31 - (x >> 24);
+	}
+#endif
+#endif
+	return topbit;
+}
+
+
+#ifdef FULLSANITYCHECKS
+static void tcsanitycheck(threadcacheblk **ptr) THROWSPEC
+{
+	assert((ptr[0] && ptr[1]) || (!ptr[0] && !ptr[1]));
+	if(ptr[0] && ptr[1])
+	{
+		assert(nedblksize(ptr[0])>=sizeof(threadcacheblk));
+		assert(nedblksize(ptr[1])>=sizeof(threadcacheblk));
+		assert(*(unsigned int *) "NEDN"==ptr[0]->magic);
+		assert(*(unsigned int *) "NEDN"==ptr[1]->magic);
+		assert(!ptr[0]->prev);
+		assert(!ptr[1]->next);
+		if(ptr[0]==ptr[1])
+		{
+			assert(!ptr[0]->next);
+			assert(!ptr[1]->prev);
+		}
+	}
+}
+static void tcfullsanitycheck(threadcache *tc) THROWSPEC
+{
+	threadcacheblk **tcbptr=tc->bins;
+	int n;
+	for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
+	{
+		threadcacheblk *b, *ob=0;
+		tcsanitycheck(tcbptr);
+		for(b=tcbptr[0]; b; ob=b, b=b->next)
+		{
+			assert(*(unsigned int *) "NEDN"==b->magic);
+			assert(!ob || ob->next==b);
+			assert(!ob || b->prev==ob);
+		}
+	}
+}
+#endif
+
+static NOINLINE void RemoveCacheEntries(nedpool *p, threadcache *tc, unsigned int age) THROWSPEC
+{
+#ifdef FULLSANITYCHECKS
+	tcfullsanitycheck(tc);
+#endif
+	if(tc->freeInCache)
+	{
+		threadcacheblk **tcbptr=tc->bins;
+		int n;
+		for(n=0; n<=THREADCACHEMAXBINS; n++, tcbptr+=2)
+		{
+			threadcacheblk **tcb=tcbptr+1;		/* come from oldest end of list */
+			/*tcsanitycheck(tcbptr);*/
+			for(; *tcb && tc->frees-(*tcb)->lastUsed>=age; )
+			{
+				threadcacheblk *f=*tcb;
+				size_t blksize=f->size; /*nedblksize(f);*/
+				assert(blksize<=nedblksize(f));
+				assert(blksize);
+#ifdef FULLSANITYCHECKS
+				assert(*(unsigned int *) "NEDN"==(*tcb)->magic);
+#endif
+				*tcb=(*tcb)->prev;
+				if(*tcb)
+					(*tcb)->next=0;
+				else
+					*tcbptr=0;
+				tc->freeInCache-=blksize;
+				assert((long) tc->freeInCache>=0);
+				mspace_free(0, f);
+				/*tcsanitycheck(tcbptr);*/
+			}
+		}
+	}
+#ifdef FULLSANITYCHECKS
+	tcfullsanitycheck(tc);
+#endif
+}
+static void DestroyCaches(nedpool *p) THROWSPEC
+{
+	if(p->caches)
+	{
+		threadcache *tc;
+		int n;
+		for(n=0; n<THREADCACHEMAXCACHES; n++)
+		{
+			if((tc=p->caches[n]))
+			{
+				tc->frees++;
+				RemoveCacheEntries(p, tc, 0);
+				assert(!tc->freeInCache);
+				tc->mymspace=-1;
+				tc->threadid=0;
+				mspace_free(0, tc);
+				p->caches[n]=0;
+			}
+		}
+	}
+}
+
+static NOINLINE threadcache *AllocCache(nedpool *p) THROWSPEC
+{
+	threadcache *tc=0;
+	int n, end;
+	ACQUIRE_LOCK(&p->mutex);
+	for(n=0; n<THREADCACHEMAXCACHES && p->caches[n]; n++);
+	if(THREADCACHEMAXCACHES==n)
+	{	/* List exhausted, so disable for this thread */
+		RELEASE_LOCK(&p->mutex);
+		return 0;
+	}
+	tc=p->caches[n]=(threadcache *) mspace_calloc(p->m[0], 1, sizeof(threadcache));
+	if(!tc)
+	{
+		RELEASE_LOCK(&p->mutex);
+		return 0;
+	}
+#ifdef FULLSANITYCHECKS
+	tc->magic1=*(unsigned int *)"NEDMALC1";
+	tc->magic2=*(unsigned int *)"NEDMALC2";
+#endif
+	tc->threadid=(long)(size_t)CURRENT_THREAD;
+	for(end=0; p->m[end]; end++);
+	tc->mymspace=tc->threadid % end;
+	RELEASE_LOCK(&p->mutex);
+	if(TLSSET(p->mycache, (void *)(size_t)(n+1))) abort();
+	return tc;
+}
+
+static void *threadcache_malloc(nedpool *p, threadcache *tc, size_t *size) THROWSPEC
+{
+	void *ret=0;
+	unsigned int bestsize;
+	unsigned int idx=size2binidx(*size);
+	size_t blksize=0;
+	threadcacheblk *blk, **binsptr;
+#ifdef FULLSANITYCHECKS
+	tcfullsanitycheck(tc);
+#endif
+	/* Calculate best fit bin size */
+	bestsize=1<<(idx+4);
+#if 0
+	/* Finer grained bin fit */
+	idx<<=1;
+	if(*size>bestsize)
+	{
+		idx++;
+		bestsize+=bestsize>>1;
+	}
+	if(*size>bestsize)
+	{
+		idx++;
+		bestsize=1<<(4+(idx>>1));
+	}
+#else
+	if(*size>bestsize)
+	{
+		idx++;
+		bestsize<<=1;
+	}
+#endif
+	assert(bestsize>=*size);
+	if(*size<bestsize) *size=bestsize;
+	assert(*size<=THREADCACHEMAX);
+	assert(idx<=THREADCACHEMAXBINS);
+	binsptr=&tc->bins[idx*2];
+	/* Try to match close, but move up a bin if necessary */
+	blk=*binsptr;
+	if(!blk || blk->size<*size)
+	{	/* Bump it up a bin */
+		if(idx<THREADCACHEMAXBINS)
+		{
+			idx++;
+			binsptr+=2;
+			blk=*binsptr;
+		}
+	}
+	if(blk)
+	{
+		blksize=blk->size; /*nedblksize(blk);*/
+		assert(nedblksize(blk)>=blksize);
+		assert(blksize>=*size);
+		if(blk->next)
+			blk->next->prev=0;
+		*binsptr=blk->next;
+		if(!*binsptr)
+			binsptr[1]=0;
+#ifdef FULLSANITYCHECKS
+		blk->magic=0;
+#endif
+		assert(binsptr[0]!=blk && binsptr[1]!=blk);
+		assert(nedblksize(blk)>=sizeof(threadcacheblk) && nedblksize(blk)<=THREADCACHEMAX+CHUNK_OVERHEAD);
+		/*printf("malloc: %p, %p, %p, %lu\n", p, tc, blk, (long) size);*/
+		ret=(void *) blk;
+	}
+	++tc->mallocs;
+	if(ret)
+	{
+		assert(blksize>=*size);
+		++tc->successes;
+		tc->freeInCache-=blksize;
+		assert((long) tc->freeInCache>=0);
+	}
+#if defined(DEBUG) && 0
+	if(!(tc->mallocs & 0xfff))
+	{
+		printf("*** threadcache=%u, mallocs=%u (%f), free=%u (%f), freeInCache=%u\n", (unsigned int) tc->threadid, tc->mallocs,
+			(float) tc->successes/tc->mallocs, tc->frees, (float) tc->successes/tc->frees, (unsigned int) tc->freeInCache);
+	}
+#endif
+#ifdef FULLSANITYCHECKS
+	tcfullsanitycheck(tc);
+#endif
+	return ret;
+}
+static NOINLINE void ReleaseFreeInCache(nedpool *p, threadcache *tc, int mymspace) THROWSPEC
+{
+	unsigned int age=THREADCACHEMAXFREESPACE/8192;
+	/*ACQUIRE_LOCK(&p->m[mymspace]->mutex);*/
+	while(age && tc->freeInCache>=THREADCACHEMAXFREESPACE)
+	{
+		RemoveCacheEntries(p, tc, age);
+		/*printf("*** Removing cache entries older than %u (%u)\n", age, (unsigned int) tc->freeInCache);*/
+		age>>=1;
+	}
+	/*RELEASE_LOCK(&p->m[mymspace]->mutex);*/
+}
+static void threadcache_free(nedpool *p, threadcache *tc, int mymspace, void *mem, size_t size) THROWSPEC
+{
+	unsigned int bestsize;
+	unsigned int idx=size2binidx(size);
+	threadcacheblk **binsptr, *tck=(threadcacheblk *) mem;
+	assert(size>=sizeof(threadcacheblk) && size<=THREADCACHEMAX+CHUNK_OVERHEAD);
+#ifdef DEBUG
+	{	/* Make sure this is a valid memory block */
+	    mchunkptr p  = mem2chunk(mem);
+	    mstate fm = get_mstate_for(p);
+	    if (!ok_magic(fm)) {
+	      USAGE_ERROR_ACTION(fm, p);
+	      return;
+	    }
+	}
+#endif
+#ifdef FULLSANITYCHECKS
+	tcfullsanitycheck(tc);
+#endif
+	/* Calculate best fit bin size */
+	bestsize=1<<(idx+4);
+#if 0
+	/* Finer grained bin fit */
+	idx<<=1;
+	if(size>bestsize)
+	{
+		unsigned int biggerbestsize=bestsize+bestsize<<1;
+		if(size>=biggerbestsize)
+		{
+			idx++;
+			bestsize=biggerbestsize;
+		}
+	}
+#endif
+	if(bestsize!=size)	/* dlmalloc can round up, so we round down to preserve indexing */
+		size=bestsize;
+	binsptr=&tc->bins[idx*2];
+	assert(idx<=THREADCACHEMAXBINS);
+	if(tck==*binsptr)
+	{
+		fprintf(stderr, "Attempt to free already freed memory block %p - aborting!\n", tck);
+		abort();
+	}
+#ifdef FULLSANITYCHECKS
+	tck->magic=*(unsigned int *) "NEDN";
+#endif
+	tck->lastUsed=++tc->frees;
+	tck->size=(unsigned int) size;
+	tck->next=*binsptr;
+	tck->prev=0;
+	if(tck->next)
+		tck->next->prev=tck;
+	else
+		binsptr[1]=tck;
+	assert(!*binsptr || (*binsptr)->size==tck->size);
+	*binsptr=tck;
+	assert(tck==tc->bins[idx*2]);
+	assert(tc->bins[idx*2+1]==tck || binsptr[0]->next->prev==tck);
+	/*printf("free: %p, %p, %p, %lu\n", p, tc, mem, (long) size);*/
+	tc->freeInCache+=size;
+#ifdef FULLSANITYCHECKS
+	tcfullsanitycheck(tc);
+#endif
+#if 1
+	if(tc->freeInCache>=THREADCACHEMAXFREESPACE)
+		ReleaseFreeInCache(p, tc, mymspace);
+#endif
+}
+
+
+
+
+static NOINLINE int InitPool(nedpool *p, size_t capacity, int threads) THROWSPEC
+{	/* threads is -1 for system pool */
+	ensure_initialization();
+	ACQUIRE_MALLOC_GLOBAL_LOCK();
+	if(p->threads) goto done;
+	if(INITIAL_LOCK(&p->mutex)) goto err;
+	if(TLSALLOC(&p->mycache)) goto err;
+	if(!(p->m[0]=(mstate) create_mspace(capacity, 1))) goto err;
+	p->m[0]->extp=p;
+	p->threads=(threads<1 || threads>MAXTHREADSINPOOL) ? MAXTHREADSINPOOL : threads;
+done:
+	RELEASE_MALLOC_GLOBAL_LOCK();
+	return 1;
+err:
+	if(threads<0)
+		abort();			/* If you can't allocate for system pool, we're screwed */
+	DestroyCaches(p);
+	if(p->m[0])
+	{
+		destroy_mspace(p->m[0]);
+		p->m[0]=0;
+	}
+	if(p->mycache)
+	{
+		if(TLSFREE(p->mycache)) abort();
+		p->mycache=0;
+	}
+	RELEASE_MALLOC_GLOBAL_LOCK();
+	return 0;
+}
+static NOINLINE mstate FindMSpace(nedpool *p, threadcache *tc, int *lastUsed, size_t size) THROWSPEC
+{	/* Gets called when thread's last used mspace is in use. The strategy
+	is to run through the list of all available mspaces looking for an
+	unlocked one and if we fail, we create a new one so long as we don't
+	exceed p->threads */
+	int n, end;
+	for(n=end=*lastUsed+1; p->m[n]; end=++n)
+	{
+		if(TRY_LOCK(&p->m[n]->mutex)) goto found;
+	}
+	for(n=0; n<*lastUsed && p->m[n]; n++)
+	{
+		if(TRY_LOCK(&p->m[n]->mutex)) goto found;
+	}
+	if(end<p->threads)
+	{
+		mstate temp;
+		if(!(temp=(mstate) create_mspace(size, 1)))
+			goto badexit;
+		/* Now we're ready to modify the lists, we lock */
+		ACQUIRE_LOCK(&p->mutex);
+		while(p->m[end] && end<p->threads)
+			end++;
+		if(end>=p->threads)
+		{	/* Drat, must destroy it now */
+			RELEASE_LOCK(&p->mutex);
+			destroy_mspace((mspace) temp);
+			goto badexit;
+		}
+		/* We really want to make sure this goes into memory now but we
+		have to be careful of breaking aliasing rules, so write it twice */
+		*((volatile struct malloc_state **) &p->m[end])=p->m[end]=temp;
+		ACQUIRE_LOCK(&p->m[end]->mutex);
+		/*printf("Created mspace idx %d\n", end);*/
+		RELEASE_LOCK(&p->mutex);
+		n=end;
+		goto found;
+	}
+	/* Let it lock on the last one it used */
+badexit:
+	ACQUIRE_LOCK(&p->m[*lastUsed]->mutex);
+	return p->m[*lastUsed];
+found:
+	*lastUsed=n;
+	if(tc)
+		tc->mymspace=n;
+	else
+	{
+		if(TLSSET(p->mycache, (void *)(size_t)(-(n+1)))) abort();
+	}
+	return p->m[n];
+}
+
+nedpool *nedcreatepool(size_t capacity, int threads) THROWSPEC
+{
+	nedpool *ret;
+	if(!(ret=(nedpool *) nedpcalloc(0, 1, sizeof(nedpool)))) return 0;
+	if(!InitPool(ret, capacity, threads))
+	{
+		nedpfree(0, ret);
+		return 0;
+	}
+	return ret;
+}
+void neddestroypool(nedpool *p) THROWSPEC
+{
+	int n;
+	ACQUIRE_LOCK(&p->mutex);
+	DestroyCaches(p);
+	for(n=0; p->m[n]; n++)
+	{
+		destroy_mspace(p->m[n]);
+		p->m[n]=0;
+	}
+	RELEASE_LOCK(&p->mutex);
+	if(TLSFREE(p->mycache)) abort();
+	nedpfree(0, p);
+}
+
+void nedpsetvalue(nedpool *p, void *v) THROWSPEC
+{
+	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+	p->uservalue=v;
+}
+void *nedgetvalue(nedpool **p, void *mem) THROWSPEC
+{
+	nedpool *np=0;
+	mchunkptr mcp=mem2chunk(mem);
+	mstate fm;
+	if(!(is_aligned(chunk2mem(mcp))) && mcp->head != FENCEPOST_HEAD) return 0;
+	if(!cinuse(mcp)) return 0;
+	if(!next_pinuse(mcp)) return 0;
+	if(!is_mmapped(mcp) && !pinuse(mcp))
+	{
+		if(next_chunk(prev_chunk(mcp))!=mcp) return 0;
+	}
+	fm=get_mstate_for(mcp);
+	if(!ok_magic(fm)) return 0;
+	if(!ok_address(fm, mcp)) return 0;
+	if(!fm->extp) return 0;
+	np=(nedpool *) fm->extp;
+	if(p) *p=np;
+	return np->uservalue;
+}
+
+void neddisablethreadcache(nedpool *p) THROWSPEC
+{
+	int mycache;
+	if(!p)
+	{
+		p=&syspool;
+		if(!syspool.threads) InitPool(&syspool, 0, -1);
+	}
+	mycache=(int)(size_t) TLSGET(p->mycache);
+	if(!mycache)
+	{	/* Set to mspace 0 */
+		if(TLSSET(p->mycache, (void *)-1)) abort();
+	}
+	else if(mycache>0)
+	{	/* Set to last used mspace */
+		threadcache *tc=p->caches[mycache-1];
+#if defined(DEBUG)
+		printf("Threadcache utilisation: %lf%% in cache with %lf%% lost to other threads\n",
+			100.0*tc->successes/tc->mallocs, 100.0*((double) tc->mallocs-tc->frees)/tc->mallocs);
+#endif
+		if(TLSSET(p->mycache, (void *)(size_t)(-tc->mymspace))) abort();
+		tc->frees++;
+		RemoveCacheEntries(p, tc, 0);
+		assert(!tc->freeInCache);
+		tc->mymspace=-1;
+		tc->threadid=0;
+		mspace_free(0, p->caches[mycache-1]);
+		p->caches[mycache-1]=0;
+	}
+}
+
+#define GETMSPACE(m,p,tc,ms,s,action)           \
+  do                                            \
+  {                                             \
+    mstate m = GetMSpace((p),(tc),(ms),(s));    \
+    action;                                     \
+    RELEASE_LOCK(&m->mutex);                    \
+  } while (0)
+
+static FORCEINLINE mstate GetMSpace(nedpool *p, threadcache *tc, int mymspace, size_t size) THROWSPEC
+{	/* Returns a locked and ready for use mspace */
+	mstate m=p->m[mymspace];
+	assert(m);
+	if(!TRY_LOCK(&p->m[mymspace]->mutex)) m=FindMSpace(p, tc, &mymspace, size);\
+	/*assert(IS_LOCKED(&p->m[mymspace]->mutex));*/
+	return m;
+}
+static FORCEINLINE void GetThreadCache(nedpool **p, threadcache **tc, int *mymspace, size_t *size) THROWSPEC
+{
+	int mycache;
+	if(size && *size<sizeof(threadcacheblk)) *size=sizeof(threadcacheblk);
+	if(!*p)
+	{
+		*p=&syspool;
+		if(!syspool.threads) InitPool(&syspool, 0, -1);
+	}
+	mycache=(int)(size_t) TLSGET((*p)->mycache);
+	if(mycache>0)
+	{
+		*tc=(*p)->caches[mycache-1];
+		*mymspace=(*tc)->mymspace;
+	}
+	else if(!mycache)
+	{
+		*tc=AllocCache(*p);
+		if(!*tc)
+		{	/* Disable */
+			if(TLSSET((*p)->mycache, (void *)-1)) abort();
+			*mymspace=0;
+		}
+		else
+			*mymspace=(*tc)->mymspace;
+	}
+	else
+	{
+		*tc=0;
+		*mymspace=-mycache-1;
+	}
+	assert(*mymspace>=0);
+	assert((long)(size_t)CURRENT_THREAD==(*tc)->threadid);
+#ifdef FULLSANITYCHECKS
+	if(*tc)
+	{
+		if(*(unsigned int *)"NEDMALC1"!=(*tc)->magic1 || *(unsigned int *)"NEDMALC2"!=(*tc)->magic2)
+		{
+			abort();
+		}
+	}
+#endif
+}
+
+void * nedpmalloc(nedpool *p, size_t size) THROWSPEC
+{
+	void *ret=0;
+	threadcache *tc;
+	int mymspace;
+	GetThreadCache(&p, &tc, &mymspace, &size);
+#if THREADCACHEMAX
+	if(tc && size<=THREADCACHEMAX)
+	{	/* Use the thread cache */
+		ret=threadcache_malloc(p, tc, &size);
+	}
+#endif
+	if(!ret)
+	{	/* Use this thread's mspace */
+	GETMSPACE(m, p, tc, mymspace, size,
+		  ret=mspace_malloc(m, size));
+	}
+	return ret;
+}
+void * nedpcalloc(nedpool *p, size_t no, size_t size) THROWSPEC
+{
+	size_t rsize=size*no;
+	void *ret=0;
+	threadcache *tc;
+	int mymspace;
+	GetThreadCache(&p, &tc, &mymspace, &rsize);
+#if THREADCACHEMAX
+	if(tc && rsize<=THREADCACHEMAX)
+	{	/* Use the thread cache */
+		if((ret=threadcache_malloc(p, tc, &rsize)))
+			memset(ret, 0, rsize);
+	}
+#endif
+	if(!ret)
+	{	/* Use this thread's mspace */
+	GETMSPACE(m, p, tc, mymspace, rsize,
+		  ret=mspace_calloc(m, 1, rsize));
+	}
+	return ret;
+}
+void * nedprealloc(nedpool *p, void *mem, size_t size) THROWSPEC
+{
+	void *ret=0;
+	threadcache *tc;
+	int mymspace;
+	if(!mem) return nedpmalloc(p, size);
+	GetThreadCache(&p, &tc, &mymspace, &size);
+#if THREADCACHEMAX
+	if(tc && size && size<=THREADCACHEMAX)
+	{	/* Use the thread cache */
+		size_t memsize=nedblksize(mem);
+		assert(memsize);
+		if((ret=threadcache_malloc(p, tc, &size)))
+		{
+			memcpy(ret, mem, memsize<size ? memsize : size);
+			if(memsize<=THREADCACHEMAX)
+				threadcache_free(p, tc, mymspace, mem, memsize);
+			else
+				mspace_free(0, mem);
+		}
+	}
+#endif
+	if(!ret)
+	{	/* Reallocs always happen in the mspace they happened in, so skip
+		locking the preferred mspace for this thread */
+		ret=mspace_realloc(0, mem, size);
+	}
+	return ret;
+}
+void   nedpfree(nedpool *p, void *mem) THROWSPEC
+{	/* Frees always happen in the mspace they happened in, so skip
+	locking the preferred mspace for this thread */
+	threadcache *tc;
+	int mymspace;
+	size_t memsize;
+	assert(mem);
+	GetThreadCache(&p, &tc, &mymspace, 0);
+#if THREADCACHEMAX
+	memsize=nedblksize(mem);
+	assert(memsize);
+	if(mem && tc && memsize<=(THREADCACHEMAX+CHUNK_OVERHEAD))
+		threadcache_free(p, tc, mymspace, mem, memsize);
+	else
+#endif
+		mspace_free(0, mem);
+}
+void * nedpmemalign(nedpool *p, size_t alignment, size_t bytes) THROWSPEC
+{
+	void *ret;
+	threadcache *tc;
+	int mymspace;
+	GetThreadCache(&p, &tc, &mymspace, &bytes);
+	{	/* Use this thread's mspace */
+	GETMSPACE(m, p, tc, mymspace, bytes,
+		  ret=mspace_memalign(m, alignment, bytes));
+	}
+	return ret;
+}
+#if !NO_MALLINFO
+struct mallinfo nedpmallinfo(nedpool *p) THROWSPEC
+{
+	int n;
+	struct mallinfo ret={0};
+	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+	for(n=0; p->m[n]; n++)
+	{
+		struct mallinfo t=mspace_mallinfo(p->m[n]);
+		ret.arena+=t.arena;
+		ret.ordblks+=t.ordblks;
+		ret.hblkhd+=t.hblkhd;
+		ret.usmblks+=t.usmblks;
+		ret.uordblks+=t.uordblks;
+		ret.fordblks+=t.fordblks;
+		ret.keepcost+=t.keepcost;
+	}
+	return ret;
+}
+#endif
+int    nedpmallopt(nedpool *p, int parno, int value) THROWSPEC
+{
+	return mspace_mallopt(parno, value);
+}
+int    nedpmalloc_trim(nedpool *p, size_t pad) THROWSPEC
+{
+	int n, ret=0;
+	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+	for(n=0; p->m[n]; n++)
+	{
+		ret+=mspace_trim(p->m[n], pad);
+	}
+	return ret;
+}
+void   nedpmalloc_stats(nedpool *p) THROWSPEC
+{
+	int n;
+	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+	for(n=0; p->m[n]; n++)
+	{
+		mspace_malloc_stats(p->m[n]);
+	}
+}
+size_t nedpmalloc_footprint(nedpool *p) THROWSPEC
+{
+	size_t ret=0;
+	int n;
+	if(!p) { p=&syspool; if(!syspool.threads) InitPool(&syspool, 0, -1); }
+	for(n=0; p->m[n]; n++)
+	{
+		ret+=mspace_footprint(p->m[n]);
+	}
+	return ret;
+}
+void **nedpindependent_calloc(nedpool *p, size_t elemsno, size_t elemsize, void **chunks) THROWSPEC
+{
+	void **ret;
+	threadcache *tc;
+	int mymspace;
+	GetThreadCache(&p, &tc, &mymspace, &elemsize);
+    GETMSPACE(m, p, tc, mymspace, elemsno*elemsize,
+	      ret=mspace_independent_calloc(m, elemsno, elemsize, chunks));
+	return ret;
+}
+void **nedpindependent_comalloc(nedpool *p, size_t elems, size_t *sizes, void **chunks) THROWSPEC
+{
+	void **ret;
+	threadcache *tc;
+	int mymspace;
+    size_t i, *adjustedsizes=(size_t *) alloca(elems*sizeof(size_t));
+    if(!adjustedsizes) return 0;
+    for(i=0; i<elems; i++)
+	adjustedsizes[i]=sizes[i]<sizeof(threadcacheblk) ? sizeof(threadcacheblk) : sizes[i];
+	GetThreadCache(&p, &tc, &mymspace, 0);
+	GETMSPACE(m, p, tc, mymspace, 0,
+	      ret=mspace_independent_comalloc(m, elems, adjustedsizes, chunks));
+	return ret;
+}
+
+#ifdef OVERRIDE_STRDUP
+/*
+ * This implementation is purely there to override the libc version, to
+ * avoid a crash due to allocation and free on different 'heaps'.
+ */
+char *strdup(const char *s1)
+{
+	char *s2 = 0;
+	if (s1) {
+		s2 = malloc(strlen(s1) + 1);
+		strcpy(s2, s1);
+	}
+	return s2;
+}
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/fast-import.c b/fast-import.c
index 65d65bf8f91b7a..3c5a269d9ef48e 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -631,8 +631,9 @@ static void *pool_calloc(size_t count, size_t size)
 
 static char *pool_strdup(const char *s)
 {
-	char *r = pool_alloc(strlen(s) + 1);
-	strcpy(r, s);
+	size_t len = strlen(s) + 1;
+	char *r = pool_alloc(len);
+	memcpy(r, s, len);
 	return r;
 }
 
diff --git a/fast-import.c.orig b/fast-import.c.orig
new file mode 100644
index 00000000000000..65d65bf8f91b7a
--- /dev/null
+++ b/fast-import.c.orig
@@ -0,0 +1,3353 @@
+/*
+(See Documentation/git-fast-import.txt for maintained documentation.)
+Format of STDIN stream:
+
+  stream ::= cmd*;
+
+  cmd ::= new_blob
+        | new_commit
+        | new_tag
+        | reset_branch
+        | checkpoint
+        | progress
+        ;
+
+  new_blob ::= 'blob' lf
+    mark?
+    file_content;
+  file_content ::= data;
+
+  new_commit ::= 'commit' sp ref_str lf
+    mark?
+    ('author' (sp name)? sp '<' email '>' sp when lf)?
+    'committer' (sp name)? sp '<' email '>' sp when lf
+    commit_msg
+    ('from' sp committish lf)?
+    ('merge' sp committish lf)*
+    (file_change | ls)*
+    lf?;
+  commit_msg ::= data;
+
+  ls ::= 'ls' sp '"' quoted(path) '"' lf;
+
+  file_change ::= file_clr
+    | file_del
+    | file_rnm
+    | file_cpy
+    | file_obm
+    | file_inm;
+  file_clr ::= 'deleteall' lf;
+  file_del ::= 'D' sp path_str lf;
+  file_rnm ::= 'R' sp path_str sp path_str lf;
+  file_cpy ::= 'C' sp path_str sp path_str lf;
+  file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
+  file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
+    data;
+  note_obm ::= 'N' sp (hexsha1 | idnum) sp committish lf;
+  note_inm ::= 'N' sp 'inline' sp committish lf
+    data;
+
+  new_tag ::= 'tag' sp tag_str lf
+    'from' sp committish lf
+    ('tagger' (sp name)? sp '<' email '>' sp when lf)?
+    tag_msg;
+  tag_msg ::= data;
+
+  reset_branch ::= 'reset' sp ref_str lf
+    ('from' sp committish lf)?
+    lf?;
+
+  checkpoint ::= 'checkpoint' lf
+    lf?;
+
+  progress ::= 'progress' sp not_lf* lf
+    lf?;
+
+     # note: the first idnum in a stream should be 1 and subsequent
+     # idnums should not have gaps between values as this will cause
+     # the stream parser to reserve space for the gapped values.  An
+     # idnum can be updated in the future to a new object by issuing
+     # a new mark directive with the old idnum.
+     #
+  mark ::= 'mark' sp idnum lf;
+  data ::= (delimited_data | exact_data)
+    lf?;
+
+    # note: delim may be any string but must not contain lf.
+    # data_line may contain any data but must not be exactly
+    # delim.
+  delimited_data ::= 'data' sp '<<' delim lf
+    (data_line lf)*
+    delim lf;
+
+     # note: declen indicates the length of binary_data in bytes.
+     # declen does not include the lf preceding the binary data.
+     #
+  exact_data ::= 'data' sp declen lf
+    binary_data;
+
+     # note: quoted strings are C-style quoting supporting \c for
+     # common escapes of 'c' (e..g \n, \t, \\, \") or \nnn where nnn
+     # is the signed byte value in octal.  Note that the only
+     # characters which must actually be escaped to protect the
+     # stream formatting is: \, " and LF.  Otherwise these values
+     # are UTF8.
+     #
+  committish  ::= (ref_str | hexsha1 | sha1exp_str | idnum);
+  ref_str     ::= ref;
+  sha1exp_str ::= sha1exp;
+  tag_str     ::= tag;
+  path_str    ::= path    | '"' quoted(path)    '"' ;
+  mode        ::= '100644' | '644'
+                | '100755' | '755'
+                | '120000'
+                ;
+
+  declen ::= # unsigned 32 bit value, ascii base10 notation;
+  bigint ::= # unsigned integer value, ascii base10 notation;
+  binary_data ::= # file content, not interpreted;
+
+  when         ::= raw_when | rfc2822_when;
+  raw_when     ::= ts sp tz;
+  rfc2822_when ::= # Valid RFC 2822 date and time;
+
+  sp ::= # ASCII space character;
+  lf ::= # ASCII newline (LF) character;
+
+     # note: a colon (':') must precede the numerical value assigned to
+     # an idnum.  This is to distinguish it from a ref or tag name as
+     # GIT does not permit ':' in ref or tag strings.
+     #
+  idnum   ::= ':' bigint;
+  path    ::= # GIT style file path, e.g. "a/b/c";
+  ref     ::= # GIT ref name, e.g. "refs/heads/MOZ_GECKO_EXPERIMENT";
+  tag     ::= # GIT tag name, e.g. "FIREFOX_1_5";
+  sha1exp ::= # Any valid GIT SHA1 expression;
+  hexsha1 ::= # SHA1 in hexadecimal format;
+
+     # note: name and email are UTF8 strings, however name must not
+     # contain '<' or lf and email must not contain any of the
+     # following: '<', '>', lf.
+     #
+  name  ::= # valid GIT author/committer name;
+  email ::= # valid GIT author/committer email;
+  ts    ::= # time since the epoch in seconds, ascii base10 notation;
+  tz    ::= # GIT style timezone;
+
+     # note: comments, ls and cat requests may appear anywhere
+     # in the input, except within a data command.  Any form
+     # of the data command always escapes the related input
+     # from comment processing.
+     #
+     # In case it is not clear, the '#' that starts the comment
+     # must be the first character on that line (an lf
+     # preceded it).
+     #
+
+  cat_blob ::= 'cat-blob' sp (hexsha1 | idnum) lf;
+  ls_tree  ::= 'ls' sp (hexsha1 | idnum) sp path_str lf;
+
+  comment ::= '#' not_lf* lf;
+  not_lf  ::= # Any byte that is not ASCII newline (LF);
+*/
+
+#include "builtin.h"
+#include "cache.h"
+#include "object.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "delta.h"
+#include "pack.h"
+#include "refs.h"
+#include "csum-file.h"
+#include "quote.h"
+#include "exec_cmd.h"
+#include "dir.h"
+
+#define PACK_ID_BITS 16
+#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
+#define DEPTH_BITS 13
+#define MAX_DEPTH ((1<<DEPTH_BITS)-1)
+
+struct object_entry {
+	struct pack_idx_entry idx;
+	struct object_entry *next;
+	uint32_t type : TYPE_BITS,
+		pack_id : PACK_ID_BITS,
+		depth : DEPTH_BITS;
+};
+
+struct object_entry_pool {
+	struct object_entry_pool *next_pool;
+	struct object_entry *next_free;
+	struct object_entry *end;
+	struct object_entry entries[FLEX_ARRAY]; /* more */
+};
+
+struct mark_set {
+	union {
+		struct object_entry *marked[1024];
+		struct mark_set *sets[1024];
+	} data;
+	unsigned int shift;
+};
+
+struct last_object {
+	struct strbuf data;
+	off_t offset;
+	unsigned int depth;
+	unsigned no_swap : 1;
+};
+
+struct mem_pool {
+	struct mem_pool *next_pool;
+	char *next_free;
+	char *end;
+	uintmax_t space[FLEX_ARRAY]; /* more */
+};
+
+struct atom_str {
+	struct atom_str *next_atom;
+	unsigned short str_len;
+	char str_dat[FLEX_ARRAY]; /* more */
+};
+
+struct tree_content;
+struct tree_entry {
+	struct tree_content *tree;
+	struct atom_str *name;
+	struct tree_entry_ms {
+		uint16_t mode;
+		unsigned char sha1[20];
+	} versions[2];
+};
+
+struct tree_content {
+	unsigned int entry_capacity; /* must match avail_tree_content */
+	unsigned int entry_count;
+	unsigned int delta_depth;
+	struct tree_entry *entries[FLEX_ARRAY]; /* more */
+};
+
+struct avail_tree_content {
+	unsigned int entry_capacity; /* must match tree_content */
+	struct avail_tree_content *next_avail;
+};
+
+struct branch {
+	struct branch *table_next_branch;
+	struct branch *active_next_branch;
+	const char *name;
+	struct tree_entry branch_tree;
+	uintmax_t last_commit;
+	uintmax_t num_notes;
+	unsigned active : 1;
+	unsigned pack_id : PACK_ID_BITS;
+	unsigned char sha1[20];
+};
+
+struct tag {
+	struct tag *next_tag;
+	const char *name;
+	unsigned int pack_id;
+	unsigned char sha1[20];
+};
+
+struct hash_list {
+	struct hash_list *next;
+	unsigned char sha1[20];
+};
+
+typedef enum {
+	WHENSPEC_RAW = 1,
+	WHENSPEC_RFC2822,
+	WHENSPEC_NOW
+} whenspec_type;
+
+struct recent_command {
+	struct recent_command *prev;
+	struct recent_command *next;
+	char *buf;
+};
+
+/* Configured limits on output */
+static unsigned long max_depth = 10;
+static off_t max_packsize;
+static uintmax_t big_file_threshold = 512 * 1024 * 1024;
+static int force_update;
+static int pack_compression_level = Z_DEFAULT_COMPRESSION;
+static int pack_compression_seen;
+
+/* Stats and misc. counters */
+static uintmax_t alloc_count;
+static uintmax_t marks_set_count;
+static uintmax_t object_count_by_type[1 << TYPE_BITS];
+static uintmax_t duplicate_count_by_type[1 << TYPE_BITS];
+static uintmax_t delta_count_by_type[1 << TYPE_BITS];
+static unsigned long object_count;
+static unsigned long branch_count;
+static unsigned long branch_load_count;
+static int failure;
+static FILE *pack_edges;
+static unsigned int show_stats = 1;
+static int global_argc;
+static const char **global_argv;
+
+/* Memory pools */
+static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
+static size_t total_allocd;
+static struct mem_pool *mem_pool;
+
+/* Atom management */
+static unsigned int atom_table_sz = 4451;
+static unsigned int atom_cnt;
+static struct atom_str **atom_table;
+
+/* The .pack file being generated */
+static unsigned int pack_id;
+static struct sha1file *pack_file;
+static struct packed_git *pack_data;
+static struct packed_git **all_packs;
+static off_t pack_size;
+
+/* Table of objects we've written. */
+static unsigned int object_entry_alloc = 5000;
+static struct object_entry_pool *blocks;
+static struct object_entry *object_table[1 << 16];
+static struct mark_set *marks;
+static const char *export_marks_file;
+static const char *import_marks_file;
+static int import_marks_file_from_stream;
+static int import_marks_file_ignore_missing;
+static int relative_marks_paths;
+
+/* Our last blob */
+static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
+
+/* Tree management */
+static unsigned int tree_entry_alloc = 1000;
+static void *avail_tree_entry;
+static unsigned int avail_tree_table_sz = 100;
+static struct avail_tree_content **avail_tree_table;
+static struct strbuf old_tree = STRBUF_INIT;
+static struct strbuf new_tree = STRBUF_INIT;
+
+/* Branch data */
+static unsigned long max_active_branches = 5;
+static unsigned long cur_active_branches;
+static unsigned long branch_table_sz = 1039;
+static struct branch **branch_table;
+static struct branch *active_branches;
+
+/* Tag data */
+static struct tag *first_tag;
+static struct tag *last_tag;
+
+/* Input stream parsing */
+static whenspec_type whenspec = WHENSPEC_RAW;
+static struct strbuf command_buf = STRBUF_INIT;
+static int unread_command_buf;
+static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL};
+static struct recent_command *cmd_tail = &cmd_hist;
+static struct recent_command *rc_free;
+static unsigned int cmd_save = 100;
+static uintmax_t next_mark;
+static struct strbuf new_data = STRBUF_INIT;
+static int seen_data_command;
+
+/* Signal handling */
+static volatile sig_atomic_t checkpoint_requested;
+
+/* Where to write output of cat-blob commands */
+static int cat_blob_fd = STDOUT_FILENO;
+
+static void parse_argv(void);
+static void parse_cat_blob(void);
+static void parse_ls(struct branch *b);
+
+static void write_branch_report(FILE *rpt, struct branch *b)
+{
+	fprintf(rpt, "%s:\n", b->name);
+
+	fprintf(rpt, "  status      :");
+	if (b->active)
+		fputs(" active", rpt);
+	if (b->branch_tree.tree)
+		fputs(" loaded", rpt);
+	if (is_null_sha1(b->branch_tree.versions[1].sha1))
+		fputs(" dirty", rpt);
+	fputc('\n', rpt);
+
+	fprintf(rpt, "  tip commit  : %s\n", sha1_to_hex(b->sha1));
+	fprintf(rpt, "  old tree    : %s\n", sha1_to_hex(b->branch_tree.versions[0].sha1));
+	fprintf(rpt, "  cur tree    : %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1));
+	fprintf(rpt, "  commit clock: %" PRIuMAX "\n", b->last_commit);
+
+	fputs("  last pack   : ", rpt);
+	if (b->pack_id < MAX_PACK_ID)
+		fprintf(rpt, "%u", b->pack_id);
+	fputc('\n', rpt);
+
+	fputc('\n', rpt);
+}
+
+static void dump_marks_helper(FILE *, uintmax_t, struct mark_set *);
+
+static void write_crash_report(const char *err)
+{
+	char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
+	FILE *rpt = fopen(loc, "w");
+	struct branch *b;
+	unsigned long lu;
+	struct recent_command *rc;
+
+	if (!rpt) {
+		error("can't write crash report %s: %s", loc, strerror(errno));
+		return;
+	}
+
+	fprintf(stderr, "fast-import: dumping crash report to %s\n", loc);
+
+	fprintf(rpt, "fast-import crash report:\n");
+	fprintf(rpt, "    fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
+	fprintf(rpt, "    parent process     : %"PRIuMAX"\n", (uintmax_t) getppid());
+	fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, DATE_LOCAL));
+	fputc('\n', rpt);
+
+	fputs("fatal: ", rpt);
+	fputs(err, rpt);
+	fputc('\n', rpt);
+
+	fputc('\n', rpt);
+	fputs("Most Recent Commands Before Crash\n", rpt);
+	fputs("---------------------------------\n", rpt);
+	for (rc = cmd_hist.next; rc != &cmd_hist; rc = rc->next) {
+		if (rc->next == &cmd_hist)
+			fputs("* ", rpt);
+		else
+			fputs("  ", rpt);
+		fputs(rc->buf, rpt);
+		fputc('\n', rpt);
+	}
+
+	fputc('\n', rpt);
+	fputs("Active Branch LRU\n", rpt);
+	fputs("-----------------\n", rpt);
+	fprintf(rpt, "    active_branches = %lu cur, %lu max\n",
+		cur_active_branches,
+		max_active_branches);
+	fputc('\n', rpt);
+	fputs("  pos  clock name\n", rpt);
+	fputs("  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n", rpt);
+	for (b = active_branches, lu = 0; b; b = b->active_next_branch)
+		fprintf(rpt, "  %2lu) %6" PRIuMAX" %s\n",
+			++lu, b->last_commit, b->name);
+
+	fputc('\n', rpt);
+	fputs("Inactive Branches\n", rpt);
+	fputs("-----------------\n", rpt);
+	for (lu = 0; lu < branch_table_sz; lu++) {
+		for (b = branch_table[lu]; b; b = b->table_next_branch)
+			write_branch_report(rpt, b);
+	}
+
+	if (first_tag) {
+		struct tag *tg;
+		fputc('\n', rpt);
+		fputs("Annotated Tags\n", rpt);
+		fputs("--------------\n", rpt);
+		for (tg = first_tag; tg; tg = tg->next_tag) {
+			fputs(sha1_to_hex(tg->sha1), rpt);
+			fputc(' ', rpt);
+			fputs(tg->name, rpt);
+			fputc('\n', rpt);
+		}
+	}
+
+	fputc('\n', rpt);
+	fputs("Marks\n", rpt);
+	fputs("-----\n", rpt);
+	if (export_marks_file)
+		fprintf(rpt, "  exported to %s\n", export_marks_file);
+	else
+		dump_marks_helper(rpt, 0, marks);
+
+	fputc('\n', rpt);
+	fputs("-------------------\n", rpt);
+	fputs("END OF CRASH REPORT\n", rpt);
+	fclose(rpt);
+}
+
+static void end_packfile(void);
+static void unkeep_all_packs(void);
+static void dump_marks(void);
+
+static NORETURN void die_nicely(const char *err, va_list params)
+{
+	static int zombie;
+	char message[2 * PATH_MAX];
+
+	vsnprintf(message, sizeof(message), err, params);
+	fputs("fatal: ", stderr);
+	fputs(message, stderr);
+	fputc('\n', stderr);
+
+	if (!zombie) {
+		zombie = 1;
+		write_crash_report(message);
+		end_packfile();
+		unkeep_all_packs();
+		dump_marks();
+	}
+	exit(128);
+}
+
+#ifndef SIGUSR1	/* Windows, for example */
+
+static void set_checkpoint_signal(void)
+{
+}
+
+#else
+
+static void checkpoint_signal(int signo)
+{
+	checkpoint_requested = 1;
+}
+
+static void set_checkpoint_signal(void)
+{
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = checkpoint_signal;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sigaction(SIGUSR1, &sa, NULL);
+}
+
+#endif
+
+static void alloc_objects(unsigned int cnt)
+{
+	struct object_entry_pool *b;
+
+	b = xmalloc(sizeof(struct object_entry_pool)
+		+ cnt * sizeof(struct object_entry));
+	b->next_pool = blocks;
+	b->next_free = b->entries;
+	b->end = b->entries + cnt;
+	blocks = b;
+	alloc_count += cnt;
+}
+
+static struct object_entry *new_object(unsigned char *sha1)
+{
+	struct object_entry *e;
+
+	if (blocks->next_free == blocks->end)
+		alloc_objects(object_entry_alloc);
+
+	e = blocks->next_free++;
+	hashcpy(e->idx.sha1, sha1);
+	return e;
+}
+
+static struct object_entry *find_object(unsigned char *sha1)
+{
+	unsigned int h = sha1[0] << 8 | sha1[1];
+	struct object_entry *e;
+	for (e = object_table[h]; e; e = e->next)
+		if (!hashcmp(sha1, e->idx.sha1))
+			return e;
+	return NULL;
+}
+
+static struct object_entry *insert_object(unsigned char *sha1)
+{
+	unsigned int h = sha1[0] << 8 | sha1[1];
+	struct object_entry *e = object_table[h];
+
+	while (e) {
+		if (!hashcmp(sha1, e->idx.sha1))
+			return e;
+		e = e->next;
+	}
+
+	e = new_object(sha1);
+	e->next = object_table[h];
+	e->idx.offset = 0;
+	object_table[h] = e;
+	return e;
+}
+
+static unsigned int hc_str(const char *s, size_t len)
+{
+	unsigned int r = 0;
+	while (len-- > 0)
+		r = r * 31 + *s++;
+	return r;
+}
+
+static void *pool_alloc(size_t len)
+{
+	struct mem_pool *p;
+	void *r;
+
+	/* round up to a 'uintmax_t' alignment */
+	if (len & (sizeof(uintmax_t) - 1))
+		len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
+
+	for (p = mem_pool; p; p = p->next_pool)
+		if ((p->end - p->next_free >= len))
+			break;
+
+	if (!p) {
+		if (len >= (mem_pool_alloc/2)) {
+			total_allocd += len;
+			return xmalloc(len);
+		}
+		total_allocd += sizeof(struct mem_pool) + mem_pool_alloc;
+		p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc);
+		p->next_pool = mem_pool;
+		p->next_free = (char *) p->space;
+		p->end = p->next_free + mem_pool_alloc;
+		mem_pool = p;
+	}
+
+	r = p->next_free;
+	p->next_free += len;
+	return r;
+}
+
+static void *pool_calloc(size_t count, size_t size)
+{
+	size_t len = count * size;
+	void *r = pool_alloc(len);
+	memset(r, 0, len);
+	return r;
+}
+
+static char *pool_strdup(const char *s)
+{
+	char *r = pool_alloc(strlen(s) + 1);
+	strcpy(r, s);
+	return r;
+}
+
+static void insert_mark(uintmax_t idnum, struct object_entry *oe)
+{
+	struct mark_set *s = marks;
+	while ((idnum >> s->shift) >= 1024) {
+		s = pool_calloc(1, sizeof(struct mark_set));
+		s->shift = marks->shift + 10;
+		s->data.sets[0] = marks;
+		marks = s;
+	}
+	while (s->shift) {
+		uintmax_t i = idnum >> s->shift;
+		idnum -= i << s->shift;
+		if (!s->data.sets[i]) {
+			s->data.sets[i] = pool_calloc(1, sizeof(struct mark_set));
+			s->data.sets[i]->shift = s->shift - 10;
+		}
+		s = s->data.sets[i];
+	}
+	if (!s->data.marked[idnum])
+		marks_set_count++;
+	s->data.marked[idnum] = oe;
+}
+
+static struct object_entry *find_mark(uintmax_t idnum)
+{
+	uintmax_t orig_idnum = idnum;
+	struct mark_set *s = marks;
+	struct object_entry *oe = NULL;
+	if ((idnum >> s->shift) < 1024) {
+		while (s && s->shift) {
+			uintmax_t i = idnum >> s->shift;
+			idnum -= i << s->shift;
+			s = s->data.sets[i];
+		}
+		if (s)
+			oe = s->data.marked[idnum];
+	}
+	if (!oe)
+		die("mark :%" PRIuMAX " not declared", orig_idnum);
+	return oe;
+}
+
+static struct atom_str *to_atom(const char *s, unsigned short len)
+{
+	unsigned int hc = hc_str(s, len) % atom_table_sz;
+	struct atom_str *c;
+
+	for (c = atom_table[hc]; c; c = c->next_atom)
+		if (c->str_len == len && !strncmp(s, c->str_dat, len))
+			return c;
+
+	c = pool_alloc(sizeof(struct atom_str) + len + 1);
+	c->str_len = len;
+	strncpy(c->str_dat, s, len);
+	c->str_dat[len] = 0;
+	c->next_atom = atom_table[hc];
+	atom_table[hc] = c;
+	atom_cnt++;
+	return c;
+}
+
+static struct branch *lookup_branch(const char *name)
+{
+	unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz;
+	struct branch *b;
+
+	for (b = branch_table[hc]; b; b = b->table_next_branch)
+		if (!strcmp(name, b->name))
+			return b;
+	return NULL;
+}
+
+static struct branch *new_branch(const char *name)
+{
+	unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz;
+	struct branch *b = lookup_branch(name);
+
+	if (b)
+		die("Invalid attempt to create duplicate branch: %s", name);
+	switch (check_ref_format(name)) {
+	case 0: break; /* its valid */
+	case CHECK_REF_FORMAT_ONELEVEL:
+		break; /* valid, but too few '/', allow anyway */
+	default:
+		die("Branch name doesn't conform to GIT standards: %s", name);
+	}
+
+	b = pool_calloc(1, sizeof(struct branch));
+	b->name = pool_strdup(name);
+	b->table_next_branch = branch_table[hc];
+	b->branch_tree.versions[0].mode = S_IFDIR;
+	b->branch_tree.versions[1].mode = S_IFDIR;
+	b->num_notes = 0;
+	b->active = 0;
+	b->pack_id = MAX_PACK_ID;
+	branch_table[hc] = b;
+	branch_count++;
+	return b;
+}
+
+static unsigned int hc_entries(unsigned int cnt)
+{
+	cnt = cnt & 7 ? (cnt / 8) + 1 : cnt / 8;
+	return cnt < avail_tree_table_sz ? cnt : avail_tree_table_sz - 1;
+}
+
+static struct tree_content *new_tree_content(unsigned int cnt)
+{
+	struct avail_tree_content *f, *l = NULL;
+	struct tree_content *t;
+	unsigned int hc = hc_entries(cnt);
+
+	for (f = avail_tree_table[hc]; f; l = f, f = f->next_avail)
+		if (f->entry_capacity >= cnt)
+			break;
+
+	if (f) {
+		if (l)
+			l->next_avail = f->next_avail;
+		else
+			avail_tree_table[hc] = f->next_avail;
+	} else {
+		cnt = cnt & 7 ? ((cnt / 8) + 1) * 8 : cnt;
+		f = pool_alloc(sizeof(*t) + sizeof(t->entries[0]) * cnt);
+		f->entry_capacity = cnt;
+	}
+
+	t = (struct tree_content*)f;
+	t->entry_count = 0;
+	t->delta_depth = 0;
+	return t;
+}
+
+static void release_tree_entry(struct tree_entry *e);
+static void release_tree_content(struct tree_content *t)
+{
+	struct avail_tree_content *f = (struct avail_tree_content*)t;
+	unsigned int hc = hc_entries(f->entry_capacity);
+	f->next_avail = avail_tree_table[hc];
+	avail_tree_table[hc] = f;
+}
+
+static void release_tree_content_recursive(struct tree_content *t)
+{
+	unsigned int i;
+	for (i = 0; i < t->entry_count; i++)
+		release_tree_entry(t->entries[i]);
+	release_tree_content(t);
+}
+
+static struct tree_content *grow_tree_content(
+	struct tree_content *t,
+	int amt)
+{
+	struct tree_content *r = new_tree_content(t->entry_count + amt);
+	r->entry_count = t->entry_count;
+	r->delta_depth = t->delta_depth;
+	memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0]));
+	release_tree_content(t);
+	return r;
+}
+
+static struct tree_entry *new_tree_entry(void)
+{
+	struct tree_entry *e;
+
+	if (!avail_tree_entry) {
+		unsigned int n = tree_entry_alloc;
+		total_allocd += n * sizeof(struct tree_entry);
+		avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry));
+		while (n-- > 1) {
+			*((void**)e) = e + 1;
+			e++;
+		}
+		*((void**)e) = NULL;
+	}
+
+	e = avail_tree_entry;
+	avail_tree_entry = *((void**)e);
+	return e;
+}
+
+static void release_tree_entry(struct tree_entry *e)
+{
+	if (e->tree)
+		release_tree_content_recursive(e->tree);
+	*((void**)e) = avail_tree_entry;
+	avail_tree_entry = e;
+}
+
+static struct tree_content *dup_tree_content(struct tree_content *s)
+{
+	struct tree_content *d;
+	struct tree_entry *a, *b;
+	unsigned int i;
+
+	if (!s)
+		return NULL;
+	d = new_tree_content(s->entry_count);
+	for (i = 0; i < s->entry_count; i++) {
+		a = s->entries[i];
+		b = new_tree_entry();
+		memcpy(b, a, sizeof(*a));
+		if (a->tree && is_null_sha1(b->versions[1].sha1))
+			b->tree = dup_tree_content(a->tree);
+		else
+			b->tree = NULL;
+		d->entries[i] = b;
+	}
+	d->entry_count = s->entry_count;
+	d->delta_depth = s->delta_depth;
+
+	return d;
+}
+
+static void start_packfile(void)
+{
+	static char tmpfile[PATH_MAX];
+	struct packed_git *p;
+	struct pack_header hdr;
+	int pack_fd;
+
+	pack_fd = odb_mkstemp(tmpfile, sizeof(tmpfile),
+			      "pack/tmp_pack_XXXXXX");
+	p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
+	strcpy(p->pack_name, tmpfile);
+	p->pack_fd = pack_fd;
+	p->do_not_close = 1;
+	pack_file = sha1fd(pack_fd, p->pack_name);
+
+	hdr.hdr_signature = htonl(PACK_SIGNATURE);
+	hdr.hdr_version = htonl(2);
+	hdr.hdr_entries = 0;
+	sha1write(pack_file, &hdr, sizeof(hdr));
+
+	pack_data = p;
+	pack_size = sizeof(hdr);
+	object_count = 0;
+
+	all_packs = xrealloc(all_packs, sizeof(*all_packs) * (pack_id + 1));
+	all_packs[pack_id] = p;
+}
+
+static const char *create_index(void)
+{
+	const char *tmpfile;
+	struct pack_idx_entry **idx, **c, **last;
+	struct object_entry *e;
+	struct object_entry_pool *o;
+
+	/* Build the table of object IDs. */
+	idx = xmalloc(object_count * sizeof(*idx));
+	c = idx;
+	for (o = blocks; o; o = o->next_pool)
+		for (e = o->next_free; e-- != o->entries;)
+			if (pack_id == e->pack_id)
+				*c++ = &e->idx;
+	last = idx + object_count;
+	if (c != last)
+		die("internal consistency error creating the index");
+
+	tmpfile = write_idx_file(NULL, idx, object_count, pack_data->sha1);
+	free(idx);
+	return tmpfile;
+}
+
+static char *keep_pack(const char *curr_index_name)
+{
+	static char name[PATH_MAX];
+	static const char *keep_msg = "fast-import";
+	int keep_fd;
+
+	keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
+	if (keep_fd < 0)
+		die_errno("cannot create keep file");
+	write_or_die(keep_fd, keep_msg, strlen(keep_msg));
+	if (close(keep_fd))
+		die_errno("failed to write keep file");
+
+	snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
+		 get_object_directory(), sha1_to_hex(pack_data->sha1));
+	if (move_temp_to_file(pack_data->pack_name, name))
+		die("cannot store pack file");
+
+	snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
+		 get_object_directory(), sha1_to_hex(pack_data->sha1));
+	if (move_temp_to_file(curr_index_name, name))
+		die("cannot store index file");
+	free((void *)curr_index_name);
+	return name;
+}
+
+static void unkeep_all_packs(void)
+{
+	static char name[PATH_MAX];
+	int k;
+
+	for (k = 0; k < pack_id; k++) {
+		struct packed_git *p = all_packs[k];
+		snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
+			 get_object_directory(), sha1_to_hex(p->sha1));
+		unlink_or_warn(name);
+	}
+}
+
+static void end_packfile(void)
+{
+	struct packed_git *old_p = pack_data, *new_p;
+
+	clear_delta_base_cache();
+	if (object_count) {
+		unsigned char cur_pack_sha1[20];
+		char *idx_name;
+		int i;
+		struct branch *b;
+		struct tag *t;
+
+		close_pack_windows(pack_data);
+		sha1close(pack_file, cur_pack_sha1, 0);
+		fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
+				    pack_data->pack_name, object_count,
+				    cur_pack_sha1, pack_size);
+		close(pack_data->pack_fd);
+		idx_name = keep_pack(create_index());
+
+		/* Register the packfile with core git's machinery. */
+		new_p = add_packed_git(idx_name, strlen(idx_name), 1);
+		if (!new_p)
+			die("core git rejected index %s", idx_name);
+		all_packs[pack_id] = new_p;
+		install_packed_git(new_p);
+
+		/* Print the boundary */
+		if (pack_edges) {
+			fprintf(pack_edges, "%s:", new_p->pack_name);
+			for (i = 0; i < branch_table_sz; i++) {
+				for (b = branch_table[i]; b; b = b->table_next_branch) {
+					if (b->pack_id == pack_id)
+						fprintf(pack_edges, " %s", sha1_to_hex(b->sha1));
+				}
+			}
+			for (t = first_tag; t; t = t->next_tag) {
+				if (t->pack_id == pack_id)
+					fprintf(pack_edges, " %s", sha1_to_hex(t->sha1));
+			}
+			fputc('\n', pack_edges);
+			fflush(pack_edges);
+		}
+
+		pack_id++;
+	}
+	else {
+		close(old_p->pack_fd);
+		unlink_or_warn(old_p->pack_name);
+	}
+	free(old_p);
+
+	/* We can't carry a delta across packfiles. */
+	strbuf_release(&last_blob.data);
+	last_blob.offset = 0;
+	last_blob.depth = 0;
+}
+
+static void cycle_packfile(void)
+{
+	end_packfile();
+	start_packfile();
+}
+
+static int store_object(
+	enum object_type type,
+	struct strbuf *dat,
+	struct last_object *last,
+	unsigned char *sha1out,
+	uintmax_t mark)
+{
+	void *out, *delta;
+	struct object_entry *e;
+	unsigned char hdr[96];
+	unsigned char sha1[20];
+	unsigned long hdrlen, deltalen;
+	git_SHA_CTX c;
+	z_stream s;
+
+	hdrlen = sprintf((char *)hdr,"%s %lu", typename(type),
+		(unsigned long)dat->len) + 1;
+	git_SHA1_Init(&c);
+	git_SHA1_Update(&c, hdr, hdrlen);
+	git_SHA1_Update(&c, dat->buf, dat->len);
+	git_SHA1_Final(sha1, &c);
+	if (sha1out)
+		hashcpy(sha1out, sha1);
+
+	e = insert_object(sha1);
+	if (mark)
+		insert_mark(mark, e);
+	if (e->idx.offset) {
+		duplicate_count_by_type[type]++;
+		return 1;
+	} else if (find_sha1_pack(sha1, packed_git)) {
+		e->type = type;
+		e->pack_id = MAX_PACK_ID;
+		e->idx.offset = 1; /* just not zero! */
+		duplicate_count_by_type[type]++;
+		return 1;
+	}
+
+	if (last && last->data.buf && last->depth < max_depth && dat->len > 20) {
+		delta = diff_delta(last->data.buf, last->data.len,
+			dat->buf, dat->len,
+			&deltalen, dat->len - 20);
+	} else
+		delta = NULL;
+
+	memset(&s, 0, sizeof(s));
+	deflateInit(&s, pack_compression_level);
+	if (delta) {
+		s.next_in = delta;
+		s.avail_in = deltalen;
+	} else {
+		s.next_in = (void *)dat->buf;
+		s.avail_in = dat->len;
+	}
+	s.avail_out = deflateBound(&s, s.avail_in);
+	s.next_out = out = xmalloc(s.avail_out);
+	while (deflate(&s, Z_FINISH) == Z_OK)
+		/* nothing */;
+	deflateEnd(&s);
+
+	/* Determine if we should auto-checkpoint. */
+	if ((max_packsize && (pack_size + 60 + s.total_out) > max_packsize)
+		|| (pack_size + 60 + s.total_out) < pack_size) {
+
+		/* This new object needs to *not* have the current pack_id. */
+		e->pack_id = pack_id + 1;
+		cycle_packfile();
+
+		/* We cannot carry a delta into the new pack. */
+		if (delta) {
+			free(delta);
+			delta = NULL;
+
+			memset(&s, 0, sizeof(s));
+			deflateInit(&s, pack_compression_level);
+			s.next_in = (void *)dat->buf;
+			s.avail_in = dat->len;
+			s.avail_out = deflateBound(&s, s.avail_in);
+			s.next_out = out = xrealloc(out, s.avail_out);
+			while (deflate(&s, Z_FINISH) == Z_OK)
+				/* nothing */;
+			deflateEnd(&s);
+		}
+	}
+
+	e->type = type;
+	e->pack_id = pack_id;
+	e->idx.offset = pack_size;
+	object_count++;
+	object_count_by_type[type]++;
+
+	crc32_begin(pack_file);
+
+	if (delta) {
+		off_t ofs = e->idx.offset - last->offset;
+		unsigned pos = sizeof(hdr) - 1;
+
+		delta_count_by_type[type]++;
+		e->depth = last->depth + 1;
+
+		hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
+		sha1write(pack_file, hdr, hdrlen);
+		pack_size += hdrlen;
+
+		hdr[pos] = ofs & 127;
+		while (ofs >>= 7)
+			hdr[--pos] = 128 | (--ofs & 127);
+		sha1write(pack_file, hdr + pos, sizeof(hdr) - pos);
+		pack_size += sizeof(hdr) - pos;
+	} else {
+		e->depth = 0;
+		hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
+		sha1write(pack_file, hdr, hdrlen);
+		pack_size += hdrlen;
+	}
+
+	sha1write(pack_file, out, s.total_out);
+	pack_size += s.total_out;
+
+	e->idx.crc32 = crc32_end(pack_file);
+
+	free(out);
+	free(delta);
+	if (last) {
+		if (last->no_swap) {
+			last->data = *dat;
+		} else {
+			strbuf_swap(&last->data, dat);
+		}
+		last->offset = e->idx.offset;
+		last->depth = e->depth;
+	}
+	return 0;
+}
+
+static void truncate_pack(off_t to, git_SHA_CTX *ctx)
+{
+	if (ftruncate(pack_data->pack_fd, to)
+	 || lseek(pack_data->pack_fd, to, SEEK_SET) != to)
+		die_errno("cannot truncate pack to skip duplicate");
+	pack_size = to;
+
+	/* yes this is a layering violation */
+	pack_file->total = to;
+	pack_file->offset = 0;
+	pack_file->ctx = *ctx;
+}
+
+static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
+{
+	size_t in_sz = 64 * 1024, out_sz = 64 * 1024;
+	unsigned char *in_buf = xmalloc(in_sz);
+	unsigned char *out_buf = xmalloc(out_sz);
+	struct object_entry *e;
+	unsigned char sha1[20];
+	unsigned long hdrlen;
+	off_t offset;
+	git_SHA_CTX c;
+	git_SHA_CTX pack_file_ctx;
+	z_stream s;
+	int status = Z_OK;
+
+	/* Determine if we should auto-checkpoint. */
+	if ((max_packsize && (pack_size + 60 + len) > max_packsize)
+		|| (pack_size + 60 + len) < pack_size)
+		cycle_packfile();
+
+	offset = pack_size;
+
+	/* preserve the pack_file SHA1 ctx in case we have to truncate later */
+	sha1flush(pack_file);
+	pack_file_ctx = pack_file->ctx;
+
+	hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
+	if (out_sz <= hdrlen)
+		die("impossibly large object header");
+
+	git_SHA1_Init(&c);
+	git_SHA1_Update(&c, out_buf, hdrlen);
+
+	crc32_begin(pack_file);
+
+	memset(&s, 0, sizeof(s));
+	deflateInit(&s, pack_compression_level);
+
+	hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
+	if (out_sz <= hdrlen)
+		die("impossibly large object header");
+
+	s.next_out = out_buf + hdrlen;
+	s.avail_out = out_sz - hdrlen;
+
+	while (status != Z_STREAM_END) {
+		if (0 < len && !s.avail_in) {
+			size_t cnt = in_sz < len ? in_sz : (size_t)len;
+			size_t n = fread(in_buf, 1, cnt, stdin);
+			if (!n && feof(stdin))
+				die("EOF in data (%" PRIuMAX " bytes remaining)", len);
+
+			git_SHA1_Update(&c, in_buf, n);
+			s.next_in = in_buf;
+			s.avail_in = n;
+			len -= n;
+		}
+
+		status = deflate(&s, len ? 0 : Z_FINISH);
+
+		if (!s.avail_out || status == Z_STREAM_END) {
+			size_t n = s.next_out - out_buf;
+			sha1write(pack_file, out_buf, n);
+			pack_size += n;
+			s.next_out = out_buf;
+			s.avail_out = out_sz;
+		}
+
+		switch (status) {
+		case Z_OK:
+		case Z_BUF_ERROR:
+		case Z_STREAM_END:
+			continue;
+		default:
+			die("unexpected deflate failure: %d", status);
+		}
+	}
+	deflateEnd(&s);
+	git_SHA1_Final(sha1, &c);
+
+	if (sha1out)
+		hashcpy(sha1out, sha1);
+
+	e = insert_object(sha1);
+
+	if (mark)
+		insert_mark(mark, e);
+
+	if (e->idx.offset) {
+		duplicate_count_by_type[OBJ_BLOB]++;
+		truncate_pack(offset, &pack_file_ctx);
+
+	} else if (find_sha1_pack(sha1, packed_git)) {
+		e->type = OBJ_BLOB;
+		e->pack_id = MAX_PACK_ID;
+		e->idx.offset = 1; /* just not zero! */
+		duplicate_count_by_type[OBJ_BLOB]++;
+		truncate_pack(offset, &pack_file_ctx);
+
+	} else {
+		e->depth = 0;
+		e->type = OBJ_BLOB;
+		e->pack_id = pack_id;
+		e->idx.offset = offset;
+		e->idx.crc32 = crc32_end(pack_file);
+		object_count++;
+		object_count_by_type[OBJ_BLOB]++;
+	}
+
+	free(in_buf);
+	free(out_buf);
+}
+
+/* All calls must be guarded by find_object() or find_mark() to
+ * ensure the 'struct object_entry' passed was written by this
+ * process instance.  We unpack the entry by the offset, avoiding
+ * the need for the corresponding .idx file.  This unpacking rule
+ * works because we only use OBJ_REF_DELTA within the packfiles
+ * created by fast-import.
+ *
+ * oe must not be NULL.  Such an oe usually comes from giving
+ * an unknown SHA-1 to find_object() or an undefined mark to
+ * find_mark().  Callers must test for this condition and use
+ * the standard read_sha1_file() when it happens.
+ *
+ * oe->pack_id must not be MAX_PACK_ID.  Such an oe is usually from
+ * find_mark(), where the mark was reloaded from an existing marks
+ * file and is referencing an object that this fast-import process
+ * instance did not write out to a packfile.  Callers must test for
+ * this condition and use read_sha1_file() instead.
+ */
+static void *gfi_unpack_entry(
+	struct object_entry *oe,
+	unsigned long *sizep)
+{
+	enum object_type type;
+	struct packed_git *p = all_packs[oe->pack_id];
+	if (p == pack_data && p->pack_size < (pack_size + 20)) {
+		/* The object is stored in the packfile we are writing to
+		 * and we have modified it since the last time we scanned
+		 * back to read a previously written object.  If an old
+		 * window covered [p->pack_size, p->pack_size + 20) its
+		 * data is stale and is not valid.  Closing all windows
+		 * and updating the packfile length ensures we can read
+		 * the newly written data.
+		 */
+		close_pack_windows(p);
+		sha1flush(pack_file);
+
+		/* We have to offer 20 bytes additional on the end of
+		 * the packfile as the core unpacker code assumes the
+		 * footer is present at the file end and must promise
+		 * at least 20 bytes within any window it maps.  But
+		 * we don't actually create the footer here.
+		 */
+		p->pack_size = pack_size + 20;
+	}
+	return unpack_entry(p, oe->idx.offset, &type, sizep);
+}
+
+static const char *get_mode(const char *str, uint16_t *modep)
+{
+	unsigned char c;
+	uint16_t mode = 0;
+
+	while ((c = *str++) != ' ') {
+		if (c < '0' || c > '7')
+			return NULL;
+		mode = (mode << 3) + (c - '0');
+	}
+	*modep = mode;
+	return str;
+}
+
+static void load_tree(struct tree_entry *root)
+{
+	unsigned char *sha1 = root->versions[1].sha1;
+	struct object_entry *myoe;
+	struct tree_content *t;
+	unsigned long size;
+	char *buf;
+	const char *c;
+
+	root->tree = t = new_tree_content(8);
+	if (is_null_sha1(sha1))
+		return;
+
+	myoe = find_object(sha1);
+	if (myoe && myoe->pack_id != MAX_PACK_ID) {
+		if (myoe->type != OBJ_TREE)
+			die("Not a tree: %s", sha1_to_hex(sha1));
+		t->delta_depth = myoe->depth;
+		buf = gfi_unpack_entry(myoe, &size);
+		if (!buf)
+			die("Can't load tree %s", sha1_to_hex(sha1));
+	} else {
+		enum object_type type;
+		buf = read_sha1_file(sha1, &type, &size);
+		if (!buf || type != OBJ_TREE)
+			die("Can't load tree %s", sha1_to_hex(sha1));
+	}
+
+	c = buf;
+	while (c != (buf + size)) {
+		struct tree_entry *e = new_tree_entry();
+
+		if (t->entry_count == t->entry_capacity)
+			root->tree = t = grow_tree_content(t, t->entry_count);
+		t->entries[t->entry_count++] = e;
+
+		e->tree = NULL;
+		c = get_mode(c, &e->versions[1].mode);
+		if (!c)
+			die("Corrupt mode in %s", sha1_to_hex(sha1));
+		e->versions[0].mode = e->versions[1].mode;
+		e->name = to_atom(c, strlen(c));
+		c += e->name->str_len + 1;
+		hashcpy(e->versions[0].sha1, (unsigned char *)c);
+		hashcpy(e->versions[1].sha1, (unsigned char *)c);
+		c += 20;
+	}
+	free(buf);
+}
+
+static int tecmp0 (const void *_a, const void *_b)
+{
+	struct tree_entry *a = *((struct tree_entry**)_a);
+	struct tree_entry *b = *((struct tree_entry**)_b);
+	return base_name_compare(
+		a->name->str_dat, a->name->str_len, a->versions[0].mode,
+		b->name->str_dat, b->name->str_len, b->versions[0].mode);
+}
+
+static int tecmp1 (const void *_a, const void *_b)
+{
+	struct tree_entry *a = *((struct tree_entry**)_a);
+	struct tree_entry *b = *((struct tree_entry**)_b);
+	return base_name_compare(
+		a->name->str_dat, a->name->str_len, a->versions[1].mode,
+		b->name->str_dat, b->name->str_len, b->versions[1].mode);
+}
+
+static void mktree(struct tree_content *t, int v, struct strbuf *b)
+{
+	size_t maxlen = 0;
+	unsigned int i;
+
+	if (!v)
+		qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0);
+	else
+		qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp1);
+
+	for (i = 0; i < t->entry_count; i++) {
+		if (t->entries[i]->versions[v].mode)
+			maxlen += t->entries[i]->name->str_len + 34;
+	}
+
+	strbuf_reset(b);
+	strbuf_grow(b, maxlen);
+	for (i = 0; i < t->entry_count; i++) {
+		struct tree_entry *e = t->entries[i];
+		if (!e->versions[v].mode)
+			continue;
+		strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode,
+					e->name->str_dat, '\0');
+		strbuf_add(b, e->versions[v].sha1, 20);
+	}
+}
+
+static void store_tree(struct tree_entry *root)
+{
+	struct tree_content *t = root->tree;
+	unsigned int i, j, del;
+	struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
+	struct object_entry *le;
+
+	if (!is_null_sha1(root->versions[1].sha1))
+		return;
+
+	for (i = 0; i < t->entry_count; i++) {
+		if (t->entries[i]->tree)
+			store_tree(t->entries[i]);
+	}
+
+	le = find_object(root->versions[0].sha1);
+	if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
+		mktree(t, 0, &old_tree);
+		lo.data = old_tree;
+		lo.offset = le->idx.offset;
+		lo.depth = t->delta_depth;
+	}
+
+	mktree(t, 1, &new_tree);
+	store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0);
+
+	t->delta_depth = lo.depth;
+	for (i = 0, j = 0, del = 0; i < t->entry_count; i++) {
+		struct tree_entry *e = t->entries[i];
+		if (e->versions[1].mode) {
+			e->versions[0].mode = e->versions[1].mode;
+			hashcpy(e->versions[0].sha1, e->versions[1].sha1);
+			t->entries[j++] = e;
+		} else {
+			release_tree_entry(e);
+			del++;
+		}
+	}
+	t->entry_count -= del;
+}
+
+static void tree_content_replace(
+	struct tree_entry *root,
+	const unsigned char *sha1,
+	const uint16_t mode,
+	struct tree_content *newtree)
+{
+	if (!S_ISDIR(mode))
+		die("Root cannot be a non-directory");
+	hashcpy(root->versions[1].sha1, sha1);
+	if (root->tree)
+		release_tree_content_recursive(root->tree);
+	root->tree = newtree;
+}
+
+static int tree_content_set(
+	struct tree_entry *root,
+	const char *p,
+	const unsigned char *sha1,
+	const uint16_t mode,
+	struct tree_content *subtree)
+{
+	struct tree_content *t;
+	const char *slash1;
+	unsigned int i, n;
+	struct tree_entry *e;
+
+	slash1 = strchr(p, '/');
+	if (slash1)
+		n = slash1 - p;
+	else
+		n = strlen(p);
+	if (!n)
+		die("Empty path component found in input");
+	if (!slash1 && !S_ISDIR(mode) && subtree)
+		die("Non-directories cannot have subtrees");
+
+	if (!root->tree)
+		load_tree(root);
+	t = root->tree;
+	for (i = 0; i < t->entry_count; i++) {
+		e = t->entries[i];
+		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+			if (!slash1) {
+				if (!S_ISDIR(mode)
+						&& e->versions[1].mode == mode
+						&& !hashcmp(e->versions[1].sha1, sha1))
+					return 0;
+				e->versions[1].mode = mode;
+				hashcpy(e->versions[1].sha1, sha1);
+				if (e->tree)
+					release_tree_content_recursive(e->tree);
+				e->tree = subtree;
+				hashclr(root->versions[1].sha1);
+				return 1;
+			}
+			if (!S_ISDIR(e->versions[1].mode)) {
+				e->tree = new_tree_content(8);
+				e->versions[1].mode = S_IFDIR;
+			}
+			if (!e->tree)
+				load_tree(e);
+			if (tree_content_set(e, slash1 + 1, sha1, mode, subtree)) {
+				hashclr(root->versions[1].sha1);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	if (t->entry_count == t->entry_capacity)
+		root->tree = t = grow_tree_content(t, t->entry_count);
+	e = new_tree_entry();
+	e->name = to_atom(p, n);
+	e->versions[0].mode = 0;
+	hashclr(e->versions[0].sha1);
+	t->entries[t->entry_count++] = e;
+	if (slash1) {
+		e->tree = new_tree_content(8);
+		e->versions[1].mode = S_IFDIR;
+		tree_content_set(e, slash1 + 1, sha1, mode, subtree);
+	} else {
+		e->tree = subtree;
+		e->versions[1].mode = mode;
+		hashcpy(e->versions[1].sha1, sha1);
+	}
+	hashclr(root->versions[1].sha1);
+	return 1;
+}
+
+static int tree_content_remove(
+	struct tree_entry *root,
+	const char *p,
+	struct tree_entry *backup_leaf)
+{
+	struct tree_content *t;
+	const char *slash1;
+	unsigned int i, n;
+	struct tree_entry *e;
+
+	slash1 = strchr(p, '/');
+	if (slash1)
+		n = slash1 - p;
+	else
+		n = strlen(p);
+
+	if (!root->tree)
+		load_tree(root);
+	t = root->tree;
+	for (i = 0; i < t->entry_count; i++) {
+		e = t->entries[i];
+		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+			if (slash1 && !S_ISDIR(e->versions[1].mode))
+				/*
+				 * If p names a file in some subdirectory, and a
+				 * file or symlink matching the name of the
+				 * parent directory of p exists, then p cannot
+				 * exist and need not be deleted.
+				 */
+				return 1;
+			if (!slash1 || !S_ISDIR(e->versions[1].mode))
+				goto del_entry;
+			if (!e->tree)
+				load_tree(e);
+			if (tree_content_remove(e, slash1 + 1, backup_leaf)) {
+				for (n = 0; n < e->tree->entry_count; n++) {
+					if (e->tree->entries[n]->versions[1].mode) {
+						hashclr(root->versions[1].sha1);
+						return 1;
+					}
+				}
+				backup_leaf = NULL;
+				goto del_entry;
+			}
+			return 0;
+		}
+	}
+	return 0;
+
+del_entry:
+	if (backup_leaf)
+		memcpy(backup_leaf, e, sizeof(*backup_leaf));
+	else if (e->tree)
+		release_tree_content_recursive(e->tree);
+	e->tree = NULL;
+	e->versions[1].mode = 0;
+	hashclr(e->versions[1].sha1);
+	hashclr(root->versions[1].sha1);
+	return 1;
+}
+
+static int tree_content_get(
+	struct tree_entry *root,
+	const char *p,
+	struct tree_entry *leaf)
+{
+	struct tree_content *t;
+	const char *slash1;
+	unsigned int i, n;
+	struct tree_entry *e;
+
+	slash1 = strchr(p, '/');
+	if (slash1)
+		n = slash1 - p;
+	else
+		n = strlen(p);
+
+	if (!root->tree)
+		load_tree(root);
+	t = root->tree;
+	for (i = 0; i < t->entry_count; i++) {
+		e = t->entries[i];
+		if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+			if (!slash1) {
+				memcpy(leaf, e, sizeof(*leaf));
+				if (e->tree && is_null_sha1(e->versions[1].sha1))
+					leaf->tree = dup_tree_content(e->tree);
+				else
+					leaf->tree = NULL;
+				return 1;
+			}
+			if (!S_ISDIR(e->versions[1].mode))
+				return 0;
+			if (!e->tree)
+				load_tree(e);
+			return tree_content_get(e, slash1 + 1, leaf);
+		}
+	}
+	return 0;
+}
+
+static int update_branch(struct branch *b)
+{
+	static const char *msg = "fast-import";
+	struct ref_lock *lock;
+	unsigned char old_sha1[20];
+
+	if (is_null_sha1(b->sha1))
+		return 0;
+	if (read_ref(b->name, old_sha1))
+		hashclr(old_sha1);
+	lock = lock_any_ref_for_update(b->name, old_sha1, 0);
+	if (!lock)
+		return error("Unable to lock %s", b->name);
+	if (!force_update && !is_null_sha1(old_sha1)) {
+		struct commit *old_cmit, *new_cmit;
+
+		old_cmit = lookup_commit_reference_gently(old_sha1, 0);
+		new_cmit = lookup_commit_reference_gently(b->sha1, 0);
+		if (!old_cmit || !new_cmit) {
+			unlock_ref(lock);
+			return error("Branch %s is missing commits.", b->name);
+		}
+
+		if (!in_merge_bases(old_cmit, &new_cmit, 1)) {
+			unlock_ref(lock);
+			warning("Not updating %s"
+				" (new tip %s does not contain %s)",
+				b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
+			return -1;
+		}
+	}
+	if (write_ref_sha1(lock, b->sha1, msg) < 0)
+		return error("Unable to update %s", b->name);
+	return 0;
+}
+
+static void dump_branches(void)
+{
+	unsigned int i;
+	struct branch *b;
+
+	for (i = 0; i < branch_table_sz; i++) {
+		for (b = branch_table[i]; b; b = b->table_next_branch)
+			failure |= update_branch(b);
+	}
+}
+
+static void dump_tags(void)
+{
+	static const char *msg = "fast-import";
+	struct tag *t;
+	struct ref_lock *lock;
+	char ref_name[PATH_MAX];
+
+	for (t = first_tag; t; t = t->next_tag) {
+		sprintf(ref_name, "tags/%s", t->name);
+		lock = lock_ref_sha1(ref_name, NULL);
+		if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
+			failure |= error("Unable to update %s", ref_name);
+	}
+}
+
+static void dump_marks_helper(FILE *f,
+	uintmax_t base,
+	struct mark_set *m)
+{
+	uintmax_t k;
+	if (m->shift) {
+		for (k = 0; k < 1024; k++) {
+			if (m->data.sets[k])
+				dump_marks_helper(f, base + (k << m->shift),
+					m->data.sets[k]);
+		}
+	} else {
+		for (k = 0; k < 1024; k++) {
+			if (m->data.marked[k])
+				fprintf(f, ":%" PRIuMAX " %s\n", base + k,
+					sha1_to_hex(m->data.marked[k]->idx.sha1));
+		}
+	}
+}
+
+static void dump_marks(void)
+{
+	static struct lock_file mark_lock;
+	int mark_fd;
+	FILE *f;
+
+	if (!export_marks_file)
+		return;
+
+	mark_fd = hold_lock_file_for_update(&mark_lock, export_marks_file, 0);
+	if (mark_fd < 0) {
+		failure |= error("Unable to write marks file %s: %s",
+			export_marks_file, strerror(errno));
+		return;
+	}
+
+	f = fdopen(mark_fd, "w");
+	if (!f) {
+		int saved_errno = errno;
+		rollback_lock_file(&mark_lock);
+		failure |= error("Unable to write marks file %s: %s",
+			export_marks_file, strerror(saved_errno));
+		return;
+	}
+
+	/*
+	 * Since the lock file was fdopen()'ed, it should not be close()'ed.
+	 * Assign -1 to the lock file descriptor so that commit_lock_file()
+	 * won't try to close() it.
+	 */
+	mark_lock.fd = -1;
+
+	dump_marks_helper(f, 0, marks);
+	if (ferror(f) || fclose(f)) {
+		int saved_errno = errno;
+		rollback_lock_file(&mark_lock);
+		failure |= error("Unable to write marks file %s: %s",
+			export_marks_file, strerror(saved_errno));
+		return;
+	}
+
+	if (commit_lock_file(&mark_lock)) {
+		int saved_errno = errno;
+		rollback_lock_file(&mark_lock);
+		failure |= error("Unable to commit marks file %s: %s",
+			export_marks_file, strerror(saved_errno));
+		return;
+	}
+}
+
+static void read_marks(void)
+{
+	char line[512];
+	FILE *f = fopen(import_marks_file, "r");
+	if (f)
+		;
+	else if (import_marks_file_ignore_missing && errno == ENOENT)
+		return; /* Marks file does not exist */
+	else
+		die_errno("cannot read '%s'", import_marks_file);
+	while (fgets(line, sizeof(line), f)) {
+		uintmax_t mark;
+		char *end;
+		unsigned char sha1[20];
+		struct object_entry *e;
+
+		end = strchr(line, '\n');
+		if (line[0] != ':' || !end)
+			die("corrupt mark line: %s", line);
+		*end = 0;
+		mark = strtoumax(line + 1, &end, 10);
+		if (!mark || end == line + 1
+			|| *end != ' ' || get_sha1(end + 1, sha1))
+			die("corrupt mark line: %s", line);
+		e = find_object(sha1);
+		if (!e) {
+			enum object_type type = sha1_object_info(sha1, NULL);
+			if (type < 0)
+				die("object not found: %s", sha1_to_hex(sha1));
+			e = insert_object(sha1);
+			e->type = type;
+			e->pack_id = MAX_PACK_ID;
+			e->idx.offset = 1; /* just not zero! */
+		}
+		insert_mark(mark, e);
+	}
+	fclose(f);
+}
+
+
+static int read_next_command(void)
+{
+	static int stdin_eof = 0;
+
+	if (stdin_eof) {
+		unread_command_buf = 0;
+		return EOF;
+	}
+
+	for (;;) {
+		if (unread_command_buf) {
+			unread_command_buf = 0;
+		} else {
+			struct recent_command *rc;
+
+			strbuf_detach(&command_buf, NULL);
+			stdin_eof = strbuf_getline(&command_buf, stdin, '\n');
+			if (stdin_eof)
+				return EOF;
+
+			if (!seen_data_command
+				&& prefixcmp(command_buf.buf, "feature ")
+				&& prefixcmp(command_buf.buf, "option ")) {
+				parse_argv();
+			}
+
+			rc = rc_free;
+			if (rc)
+				rc_free = rc->next;
+			else {
+				rc = cmd_hist.next;
+				cmd_hist.next = rc->next;
+				cmd_hist.next->prev = &cmd_hist;
+				free(rc->buf);
+			}
+
+			rc->buf = command_buf.buf;
+			rc->prev = cmd_tail;
+			rc->next = cmd_hist.prev;
+			rc->prev->next = rc;
+			cmd_tail = rc;
+		}
+		if (!prefixcmp(command_buf.buf, "cat-blob ")) {
+			parse_cat_blob();
+			continue;
+		}
+		if (command_buf.buf[0] == '#')
+			continue;
+		return 0;
+	}
+}
+
+static void skip_optional_lf(void)
+{
+	int term_char = fgetc(stdin);
+	if (term_char != '\n' && term_char != EOF)
+		ungetc(term_char, stdin);
+}
+
+static void parse_mark(void)
+{
+	if (!prefixcmp(command_buf.buf, "mark :")) {
+		next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
+		read_next_command();
+	}
+	else
+		next_mark = 0;
+}
+
+static int parse_data(struct strbuf *sb, uintmax_t limit, uintmax_t *len_res)
+{
+	strbuf_reset(sb);
+
+	if (prefixcmp(command_buf.buf, "data "))
+		die("Expected 'data n' command, found: %s", command_buf.buf);
+
+	if (!prefixcmp(command_buf.buf + 5, "<<")) {
+		char *term = xstrdup(command_buf.buf + 5 + 2);
+		size_t term_len = command_buf.len - 5 - 2;
+
+		strbuf_detach(&command_buf, NULL);
+		for (;;) {
+			if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
+				die("EOF in data (terminator '%s' not found)", term);
+			if (term_len == command_buf.len
+				&& !strcmp(term, command_buf.buf))
+				break;
+			strbuf_addbuf(sb, &command_buf);
+			strbuf_addch(sb, '\n');
+		}
+		free(term);
+	}
+	else {
+		uintmax_t len = strtoumax(command_buf.buf + 5, NULL, 10);
+		size_t n = 0, length = (size_t)len;
+
+		if (limit && limit < len) {
+			*len_res = len;
+			return 0;
+		}
+		if (length < len)
+			die("data is too large to use in this context");
+
+		while (n < length) {
+			size_t s = strbuf_fread(sb, length - n, stdin);
+			if (!s && feof(stdin))
+				die("EOF in data (%lu bytes remaining)",
+					(unsigned long)(length - n));
+			n += s;
+		}
+	}
+
+	skip_optional_lf();
+	return 1;
+}
+
+static int validate_raw_date(const char *src, char *result, int maxlen)
+{
+	const char *orig_src = src;
+	char *endp;
+	unsigned long num;
+
+	errno = 0;
+
+	num = strtoul(src, &endp, 10);
+	/* NEEDSWORK: perhaps check for reasonable values? */
+	if (errno || endp == src || *endp != ' ')
+		return -1;
+
+	src = endp + 1;
+	if (*src != '-' && *src != '+')
+		return -1;
+
+	num = strtoul(src + 1, &endp, 10);
+	if (errno || endp == src + 1 || *endp || (endp - orig_src) >= maxlen ||
+	    1400 < num)
+		return -1;
+
+	strcpy(result, orig_src);
+	return 0;
+}
+
+static char *parse_ident(const char *buf)
+{
+	const char *gt;
+	size_t name_len;
+	char *ident;
+
+	gt = strrchr(buf, '>');
+	if (!gt)
+		die("Missing > in ident string: %s", buf);
+	gt++;
+	if (*gt != ' ')
+		die("Missing space after > in ident string: %s", buf);
+	gt++;
+	name_len = gt - buf;
+	ident = xmalloc(name_len + 24);
+	strncpy(ident, buf, name_len);
+
+	switch (whenspec) {
+	case WHENSPEC_RAW:
+		if (validate_raw_date(gt, ident + name_len, 24) < 0)
+			die("Invalid raw date \"%s\" in ident: %s", gt, buf);
+		break;
+	case WHENSPEC_RFC2822:
+		if (parse_date(gt, ident + name_len, 24) < 0)
+			die("Invalid rfc2822 date \"%s\" in ident: %s", gt, buf);
+		break;
+	case WHENSPEC_NOW:
+		if (strcmp("now", gt))
+			die("Date in ident must be 'now': %s", buf);
+		datestamp(ident + name_len, 24);
+		break;
+	}
+
+	return ident;
+}
+
+static void parse_and_store_blob(
+	struct last_object *last,
+	unsigned char *sha1out,
+	uintmax_t mark)
+{
+	static struct strbuf buf = STRBUF_INIT;
+	uintmax_t len;
+
+	if (parse_data(&buf, big_file_threshold, &len))
+		store_object(OBJ_BLOB, &buf, last, sha1out, mark);
+	else {
+		if (last) {
+			strbuf_release(&last->data);
+			last->offset = 0;
+			last->depth = 0;
+		}
+		stream_blob(len, sha1out, mark);
+		skip_optional_lf();
+	}
+}
+
+static void parse_new_blob(void)
+{
+	read_next_command();
+	parse_mark();
+	parse_and_store_blob(&last_blob, NULL, next_mark);
+}
+
+static void unload_one_branch(void)
+{
+	while (cur_active_branches
+		&& cur_active_branches >= max_active_branches) {
+		uintmax_t min_commit = ULONG_MAX;
+		struct branch *e, *l = NULL, *p = NULL;
+
+		for (e = active_branches; e; e = e->active_next_branch) {
+			if (e->last_commit < min_commit) {
+				p = l;
+				min_commit = e->last_commit;
+			}
+			l = e;
+		}
+
+		if (p) {
+			e = p->active_next_branch;
+			p->active_next_branch = e->active_next_branch;
+		} else {
+			e = active_branches;
+			active_branches = e->active_next_branch;
+		}
+		e->active = 0;
+		e->active_next_branch = NULL;
+		if (e->branch_tree.tree) {
+			release_tree_content_recursive(e->branch_tree.tree);
+			e->branch_tree.tree = NULL;
+		}
+		cur_active_branches--;
+	}
+}
+
+static void load_branch(struct branch *b)
+{
+	load_tree(&b->branch_tree);
+	if (!b->active) {
+		b->active = 1;
+		b->active_next_branch = active_branches;
+		active_branches = b;
+		cur_active_branches++;
+		branch_load_count++;
+	}
+}
+
+static unsigned char convert_num_notes_to_fanout(uintmax_t num_notes)
+{
+	unsigned char fanout = 0;
+	while ((num_notes >>= 8))
+		fanout++;
+	return fanout;
+}
+
+static void construct_path_with_fanout(const char *hex_sha1,
+		unsigned char fanout, char *path)
+{
+	unsigned int i = 0, j = 0;
+	if (fanout >= 20)
+		die("Too large fanout (%u)", fanout);
+	while (fanout) {
+		path[i++] = hex_sha1[j++];
+		path[i++] = hex_sha1[j++];
+		path[i++] = '/';
+		fanout--;
+	}
+	memcpy(path + i, hex_sha1 + j, 40 - j);
+	path[i + 40 - j] = '\0';
+}
+
+static uintmax_t do_change_note_fanout(
+		struct tree_entry *orig_root, struct tree_entry *root,
+		char *hex_sha1, unsigned int hex_sha1_len,
+		char *fullpath, unsigned int fullpath_len,
+		unsigned char fanout)
+{
+	struct tree_content *t = root->tree;
+	struct tree_entry *e, leaf;
+	unsigned int i, tmp_hex_sha1_len, tmp_fullpath_len;
+	uintmax_t num_notes = 0;
+	unsigned char sha1[20];
+	char realpath[60];
+
+	for (i = 0; t && i < t->entry_count; i++) {
+		e = t->entries[i];
+		tmp_hex_sha1_len = hex_sha1_len + e->name->str_len;
+		tmp_fullpath_len = fullpath_len;
+
+		/*
+		 * We're interested in EITHER existing note entries (entries
+		 * with exactly 40 hex chars in path, not including directory
+		 * separators), OR directory entries that may contain note
+		 * entries (with < 40 hex chars in path).
+		 * Also, each path component in a note entry must be a multiple
+		 * of 2 chars.
+		 */
+		if (!e->versions[1].mode ||
+		    tmp_hex_sha1_len > 40 ||
+		    e->name->str_len % 2)
+			continue;
+
+		/* This _may_ be a note entry, or a subdir containing notes */
+		memcpy(hex_sha1 + hex_sha1_len, e->name->str_dat,
+		       e->name->str_len);
+		if (tmp_fullpath_len)
+			fullpath[tmp_fullpath_len++] = '/';
+		memcpy(fullpath + tmp_fullpath_len, e->name->str_dat,
+		       e->name->str_len);
+		tmp_fullpath_len += e->name->str_len;
+		fullpath[tmp_fullpath_len] = '\0';
+
+		if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) {
+			/* This is a note entry */
+			construct_path_with_fanout(hex_sha1, fanout, realpath);
+			if (!strcmp(fullpath, realpath)) {
+				/* Note entry is in correct location */
+				num_notes++;
+				continue;
+			}
+
+			/* Rename fullpath to realpath */
+			if (!tree_content_remove(orig_root, fullpath, &leaf))
+				die("Failed to remove path %s", fullpath);
+			tree_content_set(orig_root, realpath,
+				leaf.versions[1].sha1,
+				leaf.versions[1].mode,
+				leaf.tree);
+		} else if (S_ISDIR(e->versions[1].mode)) {
+			/* This is a subdir that may contain note entries */
+			if (!e->tree)
+				load_tree(e);
+			num_notes += do_change_note_fanout(orig_root, e,
+				hex_sha1, tmp_hex_sha1_len,
+				fullpath, tmp_fullpath_len, fanout);
+		}
+
+		/* The above may have reallocated the current tree_content */
+		t = root->tree;
+	}
+	return num_notes;
+}
+
+static uintmax_t change_note_fanout(struct tree_entry *root,
+		unsigned char fanout)
+{
+	char hex_sha1[40], path[60];
+	return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout);
+}
+
+static void file_change_m(struct branch *b)
+{
+	const char *p = command_buf.buf + 2;
+	static struct strbuf uq = STRBUF_INIT;
+	const char *endp;
+	struct object_entry *oe = oe;
+	unsigned char sha1[20];
+	uint16_t mode, inline_data = 0;
+
+	p = get_mode(p, &mode);
+	if (!p)
+		die("Corrupt mode: %s", command_buf.buf);
+	switch (mode) {
+	case 0644:
+	case 0755:
+		mode |= S_IFREG;
+	case S_IFREG | 0644:
+	case S_IFREG | 0755:
+	case S_IFLNK:
+	case S_IFDIR:
+	case S_IFGITLINK:
+		/* ok */
+		break;
+	default:
+		die("Corrupt mode: %s", command_buf.buf);
+	}
+
+	if (*p == ':') {
+		char *x;
+		oe = find_mark(strtoumax(p + 1, &x, 10));
+		hashcpy(sha1, oe->idx.sha1);
+		p = x;
+	} else if (!prefixcmp(p, "inline")) {
+		inline_data = 1;
+		p += 6;
+	} else {
+		if (get_sha1_hex(p, sha1))
+			die("Invalid SHA1: %s", command_buf.buf);
+		oe = find_object(sha1);
+		p += 40;
+	}
+	if (*p++ != ' ')
+		die("Missing space after SHA1: %s", command_buf.buf);
+
+	strbuf_reset(&uq);
+	if (!unquote_c_style(&uq, p, &endp)) {
+		if (*endp)
+			die("Garbage after path in: %s", command_buf.buf);
+		p = uq.buf;
+	}
+
+	/* Git does not track empty, non-toplevel directories. */
+	if (S_ISDIR(mode) && !memcmp(sha1, EMPTY_TREE_SHA1_BIN, 20) && *p) {
+		tree_content_remove(&b->branch_tree, p, NULL);
+		return;
+	}
+
+	if (S_ISGITLINK(mode)) {
+		if (inline_data)
+			die("Git links cannot be specified 'inline': %s",
+				command_buf.buf);
+		else if (oe) {
+			if (oe->type != OBJ_COMMIT)
+				die("Not a commit (actually a %s): %s",
+					typename(oe->type), command_buf.buf);
+		}
+		/*
+		 * Accept the sha1 without checking; it expected to be in
+		 * another repository.
+		 */
+	} else if (inline_data) {
+		if (S_ISDIR(mode))
+			die("Directories cannot be specified 'inline': %s",
+				command_buf.buf);
+		if (p != uq.buf) {
+			strbuf_addstr(&uq, p);
+			p = uq.buf;
+		}
+		read_next_command();
+		parse_and_store_blob(&last_blob, sha1, 0);
+	} else {
+		enum object_type expected = S_ISDIR(mode) ?
+						OBJ_TREE: OBJ_BLOB;
+		enum object_type type = oe ? oe->type :
+					sha1_object_info(sha1, NULL);
+		if (type < 0)
+			die("%s not found: %s",
+					S_ISDIR(mode) ?  "Tree" : "Blob",
+					command_buf.buf);
+		if (type != expected)
+			die("Not a %s (actually a %s): %s",
+				typename(expected), typename(type),
+				command_buf.buf);
+	}
+
+	if (!*p) {
+		tree_content_replace(&b->branch_tree, sha1, mode, NULL);
+		return;
+	}
+	tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
+}
+
+static void file_change_d(struct branch *b)
+{
+	const char *p = command_buf.buf + 2;
+	static struct strbuf uq = STRBUF_INIT;
+	const char *endp;
+
+	strbuf_reset(&uq);
+	if (!unquote_c_style(&uq, p, &endp)) {
+		if (*endp)
+			die("Garbage after path in: %s", command_buf.buf);
+		p = uq.buf;
+	}
+	tree_content_remove(&b->branch_tree, p, NULL);
+}
+
+static void file_change_cr(struct branch *b, int rename)
+{
+	const char *s, *d;
+	static struct strbuf s_uq = STRBUF_INIT;
+	static struct strbuf d_uq = STRBUF_INIT;
+	const char *endp;
+	struct tree_entry leaf;
+
+	s = command_buf.buf + 2;
+	strbuf_reset(&s_uq);
+	if (!unquote_c_style(&s_uq, s, &endp)) {
+		if (*endp != ' ')
+			die("Missing space after source: %s", command_buf.buf);
+	} else {
+		endp = strchr(s, ' ');
+		if (!endp)
+			die("Missing space after source: %s", command_buf.buf);
+		strbuf_add(&s_uq, s, endp - s);
+	}
+	s = s_uq.buf;
+
+	endp++;
+	if (!*endp)
+		die("Missing dest: %s", command_buf.buf);
+
+	d = endp;
+	strbuf_reset(&d_uq);
+	if (!unquote_c_style(&d_uq, d, &endp)) {
+		if (*endp)
+			die("Garbage after dest in: %s", command_buf.buf);
+		d = d_uq.buf;
+	}
+
+	memset(&leaf, 0, sizeof(leaf));
+	if (rename)
+		tree_content_remove(&b->branch_tree, s, &leaf);
+	else
+		tree_content_get(&b->branch_tree, s, &leaf);
+	if (!leaf.versions[1].mode)
+		die("Path %s not in branch", s);
+	if (!*d) {	/* C "path/to/subdir" "" */
+		tree_content_replace(&b->branch_tree,
+			leaf.versions[1].sha1,
+			leaf.versions[1].mode,
+			leaf.tree);
+		return;
+	}
+	tree_content_set(&b->branch_tree, d,
+		leaf.versions[1].sha1,
+		leaf.versions[1].mode,
+		leaf.tree);
+}
+
+static void note_change_n(struct branch *b, unsigned char old_fanout)
+{
+	const char *p = command_buf.buf + 2;
+	static struct strbuf uq = STRBUF_INIT;
+	struct object_entry *oe = oe;
+	struct branch *s;
+	unsigned char sha1[20], commit_sha1[20];
+	char path[60];
+	uint16_t inline_data = 0;
+	unsigned char new_fanout;
+
+	/* <dataref> or 'inline' */
+	if (*p == ':') {
+		char *x;
+		oe = find_mark(strtoumax(p + 1, &x, 10));
+		hashcpy(sha1, oe->idx.sha1);
+		p = x;
+	} else if (!prefixcmp(p, "inline")) {
+		inline_data = 1;
+		p += 6;
+	} else {
+		if (get_sha1_hex(p, sha1))
+			die("Invalid SHA1: %s", command_buf.buf);
+		oe = find_object(sha1);
+		p += 40;
+	}
+	if (*p++ != ' ')
+		die("Missing space after SHA1: %s", command_buf.buf);
+
+	/* <committish> */
+	s = lookup_branch(p);
+	if (s) {
+		hashcpy(commit_sha1, s->sha1);
+	} else if (*p == ':') {
+		uintmax_t commit_mark = strtoumax(p + 1, NULL, 10);
+		struct object_entry *commit_oe = find_mark(commit_mark);
+		if (commit_oe->type != OBJ_COMMIT)
+			die("Mark :%" PRIuMAX " not a commit", commit_mark);
+		hashcpy(commit_sha1, commit_oe->idx.sha1);
+	} else if (!get_sha1(p, commit_sha1)) {
+		unsigned long size;
+		char *buf = read_object_with_reference(commit_sha1,
+			commit_type, &size, commit_sha1);
+		if (!buf || size < 46)
+			die("Not a valid commit: %s", p);
+		free(buf);
+	} else
+		die("Invalid ref name or SHA1 expression: %s", p);
+
+	if (inline_data) {
+		if (p != uq.buf) {
+			strbuf_addstr(&uq, p);
+			p = uq.buf;
+		}
+		read_next_command();
+		parse_and_store_blob(&last_blob, sha1, 0);
+	} else if (oe) {
+		if (oe->type != OBJ_BLOB)
+			die("Not a blob (actually a %s): %s",
+				typename(oe->type), command_buf.buf);
+	} else if (!is_null_sha1(sha1)) {
+		enum object_type type = sha1_object_info(sha1, NULL);
+		if (type < 0)
+			die("Blob not found: %s", command_buf.buf);
+		if (type != OBJ_BLOB)
+			die("Not a blob (actually a %s): %s",
+			    typename(type), command_buf.buf);
+	}
+
+	construct_path_with_fanout(sha1_to_hex(commit_sha1), old_fanout, path);
+	if (tree_content_remove(&b->branch_tree, path, NULL))
+		b->num_notes--;
+
+	if (is_null_sha1(sha1))
+		return; /* nothing to insert */
+
+	b->num_notes++;
+	new_fanout = convert_num_notes_to_fanout(b->num_notes);
+	construct_path_with_fanout(sha1_to_hex(commit_sha1), new_fanout, path);
+	tree_content_set(&b->branch_tree, path, sha1, S_IFREG | 0644, NULL);
+}
+
+static void file_change_deleteall(struct branch *b)
+{
+	release_tree_content_recursive(b->branch_tree.tree);
+	hashclr(b->branch_tree.versions[0].sha1);
+	hashclr(b->branch_tree.versions[1].sha1);
+	load_tree(&b->branch_tree);
+	b->num_notes = 0;
+}
+
+static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
+{
+	if (!buf || size < 46)
+		die("Not a valid commit: %s", sha1_to_hex(b->sha1));
+	if (memcmp("tree ", buf, 5)
+		|| get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1))
+		die("The commit %s is corrupt", sha1_to_hex(b->sha1));
+	hashcpy(b->branch_tree.versions[0].sha1,
+		b->branch_tree.versions[1].sha1);
+}
+
+static void parse_from_existing(struct branch *b)
+{
+	if (is_null_sha1(b->sha1)) {
+		hashclr(b->branch_tree.versions[0].sha1);
+		hashclr(b->branch_tree.versions[1].sha1);
+	} else {
+		unsigned long size;
+		char *buf;
+
+		buf = read_object_with_reference(b->sha1,
+			commit_type, &size, b->sha1);
+		parse_from_commit(b, buf, size);
+		free(buf);
+	}
+}
+
+static int parse_from(struct branch *b)
+{
+	const char *from;
+	struct branch *s;
+
+	if (prefixcmp(command_buf.buf, "from "))
+		return 0;
+
+	if (b->branch_tree.tree) {
+		release_tree_content_recursive(b->branch_tree.tree);
+		b->branch_tree.tree = NULL;
+	}
+
+	from = strchr(command_buf.buf, ' ') + 1;
+	s = lookup_branch(from);
+	if (b == s)
+		die("Can't create a branch from itself: %s", b->name);
+	else if (s) {
+		unsigned char *t = s->branch_tree.versions[1].sha1;
+		hashcpy(b->sha1, s->sha1);
+		hashcpy(b->branch_tree.versions[0].sha1, t);
+		hashcpy(b->branch_tree.versions[1].sha1, t);
+	} else if (*from == ':') {
+		uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+		struct object_entry *oe = find_mark(idnum);
+		if (oe->type != OBJ_COMMIT)
+			die("Mark :%" PRIuMAX " not a commit", idnum);
+		hashcpy(b->sha1, oe->idx.sha1);
+		if (oe->pack_id != MAX_PACK_ID) {
+			unsigned long size;
+			char *buf = gfi_unpack_entry(oe, &size);
+			parse_from_commit(b, buf, size);
+			free(buf);
+		} else
+			parse_from_existing(b);
+	} else if (!get_sha1(from, b->sha1))
+		parse_from_existing(b);
+	else
+		die("Invalid ref name or SHA1 expression: %s", from);
+
+	read_next_command();
+	return 1;
+}
+
+static struct hash_list *parse_merge(unsigned int *count)
+{
+	struct hash_list *list = NULL, *n, *e = e;
+	const char *from;
+	struct branch *s;
+
+	*count = 0;
+	while (!prefixcmp(command_buf.buf, "merge ")) {
+		from = strchr(command_buf.buf, ' ') + 1;
+		n = xmalloc(sizeof(*n));
+		s = lookup_branch(from);
+		if (s)
+			hashcpy(n->sha1, s->sha1);
+		else if (*from == ':') {
+			uintmax_t idnum = strtoumax(from + 1, NULL, 10);
+			struct object_entry *oe = find_mark(idnum);
+			if (oe->type != OBJ_COMMIT)
+				die("Mark :%" PRIuMAX " not a commit", idnum);
+			hashcpy(n->sha1, oe->idx.sha1);
+		} else if (!get_sha1(from, n->sha1)) {
+			unsigned long size;
+			char *buf = read_object_with_reference(n->sha1,
+				commit_type, &size, n->sha1);
+			if (!buf || size < 46)
+				die("Not a valid commit: %s", from);
+			free(buf);
+		} else
+			die("Invalid ref name or SHA1 expression: %s", from);
+
+		n->next = NULL;
+		if (list)
+			e->next = n;
+		else
+			list = n;
+		e = n;
+		(*count)++;
+		read_next_command();
+	}
+	return list;
+}
+
+static void parse_new_commit(void)
+{
+	static struct strbuf msg = STRBUF_INIT;
+	struct branch *b;
+	char *sp;
+	char *author = NULL;
+	char *committer = NULL;
+	struct hash_list *merge_list = NULL;
+	unsigned int merge_count;
+	unsigned char prev_fanout, new_fanout;
+
+	/* Obtain the branch name from the rest of our command */
+	sp = strchr(command_buf.buf, ' ') + 1;
+	b = lookup_branch(sp);
+	if (!b)
+		b = new_branch(sp);
+
+	read_next_command();
+	parse_mark();
+	if (!prefixcmp(command_buf.buf, "author ")) {
+		author = parse_ident(command_buf.buf + 7);
+		read_next_command();
+	}
+	if (!prefixcmp(command_buf.buf, "committer ")) {
+		committer = parse_ident(command_buf.buf + 10);
+		read_next_command();
+	}
+	if (!committer)
+		die("Expected committer but didn't get one");
+	parse_data(&msg, 0, NULL);
+	read_next_command();
+	parse_from(b);
+	merge_list = parse_merge(&merge_count);
+
+	/* ensure the branch is active/loaded */
+	if (!b->branch_tree.tree || !max_active_branches) {
+		unload_one_branch();
+		load_branch(b);
+	}
+
+	prev_fanout = convert_num_notes_to_fanout(b->num_notes);
+
+	/* file_change* */
+	while (command_buf.len > 0) {
+		if (!prefixcmp(command_buf.buf, "M "))
+			file_change_m(b);
+		else if (!prefixcmp(command_buf.buf, "D "))
+			file_change_d(b);
+		else if (!prefixcmp(command_buf.buf, "R "))
+			file_change_cr(b, 1);
+		else if (!prefixcmp(command_buf.buf, "C "))
+			file_change_cr(b, 0);
+		else if (!prefixcmp(command_buf.buf, "N "))
+			note_change_n(b, prev_fanout);
+		else if (!strcmp("deleteall", command_buf.buf))
+			file_change_deleteall(b);
+		else if (!prefixcmp(command_buf.buf, "ls "))
+			parse_ls(b);
+		else {
+			unread_command_buf = 1;
+			break;
+		}
+		if (read_next_command() == EOF)
+			break;
+	}
+
+	new_fanout = convert_num_notes_to_fanout(b->num_notes);
+	if (new_fanout != prev_fanout)
+		b->num_notes = change_note_fanout(&b->branch_tree, new_fanout);
+
+	/* build the tree and the commit */
+	store_tree(&b->branch_tree);
+	hashcpy(b->branch_tree.versions[0].sha1,
+		b->branch_tree.versions[1].sha1);
+
+	strbuf_reset(&new_data);
+	strbuf_addf(&new_data, "tree %s\n",
+		sha1_to_hex(b->branch_tree.versions[1].sha1));
+	if (!is_null_sha1(b->sha1))
+		strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1));
+	while (merge_list) {
+		struct hash_list *next = merge_list->next;
+		strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1));
+		free(merge_list);
+		merge_list = next;
+	}
+	strbuf_addf(&new_data,
+		"author %s\n"
+		"committer %s\n"
+		"\n",
+		author ? author : committer, committer);
+	strbuf_addbuf(&new_data, &msg);
+	free(author);
+	free(committer);
+
+	if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark))
+		b->pack_id = pack_id;
+	b->last_commit = object_count_by_type[OBJ_COMMIT];
+}
+
+static void parse_new_tag(void)
+{
+	static struct strbuf msg = STRBUF_INIT;
+	char *sp;
+	const char *from;
+	char *tagger;
+	struct branch *s;
+	struct tag *t;
+	uintmax_t from_mark = 0;
+	unsigned char sha1[20];
+	enum object_type type;
+
+	/* Obtain the new tag name from the rest of our command */
+	sp = strchr(command_buf.buf, ' ') + 1;
+	t = pool_alloc(sizeof(struct tag));
+	t->next_tag = NULL;
+	t->name = pool_strdup(sp);
+	if (last_tag)
+		last_tag->next_tag = t;
+	else
+		first_tag = t;
+	last_tag = t;
+	read_next_command();
+
+	/* from ... */
+	if (prefixcmp(command_buf.buf, "from "))
+		die("Expected from command, got %s", command_buf.buf);
+	from = strchr(command_buf.buf, ' ') + 1;
+	s = lookup_branch(from);
+	if (s) {
+		hashcpy(sha1, s->sha1);
+		type = OBJ_COMMIT;
+	} else if (*from == ':') {
+		struct object_entry *oe;
+		from_mark = strtoumax(from + 1, NULL, 10);
+		oe = find_mark(from_mark);
+		type = oe->type;
+		hashcpy(sha1, oe->idx.sha1);
+	} else if (!get_sha1(from, sha1)) {
+		unsigned long size;
+		char *buf;
+
+		buf = read_sha1_file(sha1, &type, &size);
+		if (!buf || size < 46)
+			die("Not a valid commit: %s", from);
+		free(buf);
+	} else
+		die("Invalid ref name or SHA1 expression: %s", from);
+	read_next_command();
+
+	/* tagger ... */
+	if (!prefixcmp(command_buf.buf, "tagger ")) {
+		tagger = parse_ident(command_buf.buf + 7);
+		read_next_command();
+	} else
+		tagger = NULL;
+
+	/* tag payload/message */
+	parse_data(&msg, 0, NULL);
+
+	/* build the tag object */
+	strbuf_reset(&new_data);
+
+	strbuf_addf(&new_data,
+		    "object %s\n"
+		    "type %s\n"
+		    "tag %s\n",
+		    sha1_to_hex(sha1), typename(type), t->name);
+	if (tagger)
+		strbuf_addf(&new_data,
+			    "tagger %s\n", tagger);
+	strbuf_addch(&new_data, '\n');
+	strbuf_addbuf(&new_data, &msg);
+	free(tagger);
+
+	if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0))
+		t->pack_id = MAX_PACK_ID;
+	else
+		t->pack_id = pack_id;
+}
+
+static void parse_reset_branch(void)
+{
+	struct branch *b;
+	char *sp;
+
+	/* Obtain the branch name from the rest of our command */
+	sp = strchr(command_buf.buf, ' ') + 1;
+	b = lookup_branch(sp);
+	if (b) {
+		hashclr(b->sha1);
+		hashclr(b->branch_tree.versions[0].sha1);
+		hashclr(b->branch_tree.versions[1].sha1);
+		if (b->branch_tree.tree) {
+			release_tree_content_recursive(b->branch_tree.tree);
+			b->branch_tree.tree = NULL;
+		}
+	}
+	else
+		b = new_branch(sp);
+	read_next_command();
+	parse_from(b);
+	if (command_buf.len > 0)
+		unread_command_buf = 1;
+}
+
+static void cat_blob_write(const char *buf, unsigned long size)
+{
+	if (write_in_full(cat_blob_fd, buf, size) != size)
+		die_errno("Write to frontend failed");
+}
+
+static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
+{
+	struct strbuf line = STRBUF_INIT;
+	unsigned long size;
+	enum object_type type = 0;
+	char *buf;
+
+	if (!oe || oe->pack_id == MAX_PACK_ID) {
+		buf = read_sha1_file(sha1, &type, &size);
+	} else {
+		type = oe->type;
+		buf = gfi_unpack_entry(oe, &size);
+	}
+
+	/*
+	 * Output based on batch_one_object() from cat-file.c.
+	 */
+	if (type <= 0) {
+		strbuf_reset(&line);
+		strbuf_addf(&line, "%s missing\n", sha1_to_hex(sha1));
+		cat_blob_write(line.buf, line.len);
+		strbuf_release(&line);
+		free(buf);
+		return;
+	}
+	if (!buf)
+		die("Can't read object %s", sha1_to_hex(sha1));
+	if (type != OBJ_BLOB)
+		die("Object %s is a %s but a blob was expected.",
+		    sha1_to_hex(sha1), typename(type));
+	strbuf_reset(&line);
+	strbuf_addf(&line, "%s %s %lu\n", sha1_to_hex(sha1),
+						typename(type), size);
+	cat_blob_write(line.buf, line.len);
+	strbuf_release(&line);
+	cat_blob_write(buf, size);
+	cat_blob_write("\n", 1);
+	free(buf);
+}
+
+static void parse_cat_blob(void)
+{
+	const char *p;
+	struct object_entry *oe = oe;
+	unsigned char sha1[20];
+
+	/* cat-blob SP <object> LF */
+	p = command_buf.buf + strlen("cat-blob ");
+	if (*p == ':') {
+		char *x;
+		oe = find_mark(strtoumax(p + 1, &x, 10));
+		if (x == p + 1)
+			die("Invalid mark: %s", command_buf.buf);
+		if (!oe)
+			die("Unknown mark: %s", command_buf.buf);
+		if (*x)
+			die("Garbage after mark: %s", command_buf.buf);
+		hashcpy(sha1, oe->idx.sha1);
+	} else {
+		if (get_sha1_hex(p, sha1))
+			die("Invalid SHA1: %s", command_buf.buf);
+		if (p[40])
+			die("Garbage after SHA1: %s", command_buf.buf);
+		oe = find_object(sha1);
+	}
+
+	cat_blob(oe, sha1);
+}
+
+static struct object_entry *dereference(struct object_entry *oe,
+					unsigned char sha1[20])
+{
+	unsigned long size;
+	char *buf = NULL;
+	if (!oe) {
+		enum object_type type = sha1_object_info(sha1, NULL);
+		if (type < 0)
+			die("object not found: %s", sha1_to_hex(sha1));
+		/* cache it! */
+		oe = insert_object(sha1);
+		oe->type = type;
+		oe->pack_id = MAX_PACK_ID;
+		oe->idx.offset = 1;
+	}
+	switch (oe->type) {
+	case OBJ_TREE:	/* easy case. */
+		return oe;
+	case OBJ_COMMIT:
+	case OBJ_TAG:
+		break;
+	default:
+		die("Not a treeish: %s", command_buf.buf);
+	}
+
+	if (oe->pack_id != MAX_PACK_ID) {	/* in a pack being written */
+		buf = gfi_unpack_entry(oe, &size);
+	} else {
+		enum object_type unused;
+		buf = read_sha1_file(sha1, &unused, &size);
+	}
+	if (!buf)
+		die("Can't load object %s", sha1_to_hex(sha1));
+
+	/* Peel one layer. */
+	switch (oe->type) {
+	case OBJ_TAG:
+		if (size < 40 + strlen("object ") ||
+		    get_sha1_hex(buf + strlen("object "), sha1))
+			die("Invalid SHA1 in tag: %s", command_buf.buf);
+		break;
+	case OBJ_COMMIT:
+		if (size < 40 + strlen("tree ") ||
+		    get_sha1_hex(buf + strlen("tree "), sha1))
+			die("Invalid SHA1 in commit: %s", command_buf.buf);
+	}
+
+	free(buf);
+	return find_object(sha1);
+}
+
+static struct object_entry *parse_treeish_dataref(const char **p)
+{
+	unsigned char sha1[20];
+	struct object_entry *e;
+
+	if (**p == ':') {	/* <mark> */
+		char *endptr;
+		e = find_mark(strtoumax(*p + 1, &endptr, 10));
+		if (endptr == *p + 1)
+			die("Invalid mark: %s", command_buf.buf);
+		if (!e)
+			die("Unknown mark: %s", command_buf.buf);
+		*p = endptr;
+		hashcpy(sha1, e->idx.sha1);
+	} else {	/* <sha1> */
+		if (get_sha1_hex(*p, sha1))
+			die("Invalid SHA1: %s", command_buf.buf);
+		e = find_object(sha1);
+		*p += 40;
+	}
+
+	while (!e || e->type != OBJ_TREE)
+		e = dereference(e, sha1);
+	return e;
+}
+
+static void print_ls(int mode, const unsigned char *sha1, const char *path)
+{
+	static struct strbuf line = STRBUF_INIT;
+
+	/* See show_tree(). */
+	const char *type =
+		S_ISGITLINK(mode) ? commit_type :
+		S_ISDIR(mode) ? tree_type :
+		blob_type;
+
+	if (!mode) {
+		/* missing SP path LF */
+		strbuf_reset(&line);
+		strbuf_addstr(&line, "missing ");
+		quote_c_style(path, &line, NULL, 0);
+		strbuf_addch(&line, '\n');
+	} else {
+		/* mode SP type SP object_name TAB path LF */
+		strbuf_reset(&line);
+		strbuf_addf(&line, "%06o %s %s\t",
+				mode, type, sha1_to_hex(sha1));
+		quote_c_style(path, &line, NULL, 0);
+		strbuf_addch(&line, '\n');
+	}
+	cat_blob_write(line.buf, line.len);
+}
+
+static void parse_ls(struct branch *b)
+{
+	const char *p;
+	struct tree_entry *root = NULL;
+	struct tree_entry leaf = {NULL};
+
+	/* ls SP (<treeish> SP)? <path> */
+	p = command_buf.buf + strlen("ls ");
+	if (*p == '"') {
+		if (!b)
+			die("Not in a commit: %s", command_buf.buf);
+		root = &b->branch_tree;
+	} else {
+		struct object_entry *e = parse_treeish_dataref(&p);
+		root = new_tree_entry();
+		hashcpy(root->versions[1].sha1, e->idx.sha1);
+		load_tree(root);
+		if (*p++ != ' ')
+			die("Missing space after tree-ish: %s", command_buf.buf);
+	}
+	if (*p == '"') {
+		static struct strbuf uq = STRBUF_INIT;
+		const char *endp;
+		strbuf_reset(&uq);
+		if (unquote_c_style(&uq, p, &endp))
+			die("Invalid path: %s", command_buf.buf);
+		if (*endp)
+			die("Garbage after path in: %s", command_buf.buf);
+		p = uq.buf;
+	}
+	tree_content_get(root, p, &leaf);
+	/*
+	 * A directory in preparation would have a sha1 of zero
+	 * until it is saved.  Save, for simplicity.
+	 */
+	if (S_ISDIR(leaf.versions[1].mode))
+		store_tree(&leaf);
+
+	print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);
+	if (!b || root != &b->branch_tree)
+		release_tree_entry(root);
+}
+
+static void checkpoint(void)
+{
+	checkpoint_requested = 0;
+	if (object_count) {
+		cycle_packfile();
+		dump_branches();
+		dump_tags();
+		dump_marks();
+	}
+}
+
+static void parse_checkpoint(void)
+{
+	checkpoint_requested = 1;
+	skip_optional_lf();
+}
+
+static void parse_progress(void)
+{
+	fwrite(command_buf.buf, 1, command_buf.len, stdout);
+	fputc('\n', stdout);
+	fflush(stdout);
+	skip_optional_lf();
+}
+
+static char* make_fast_import_path(const char *path)
+{
+	struct strbuf abs_path = STRBUF_INIT;
+
+	if (!relative_marks_paths || is_absolute_path(path))
+		return xstrdup(path);
+	strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path);
+	return strbuf_detach(&abs_path, NULL);
+}
+
+static void option_import_marks(const char *marks,
+					int from_stream, int ignore_missing)
+{
+	if (import_marks_file) {
+		if (from_stream)
+			die("Only one import-marks command allowed per stream");
+
+		/* read previous mark file */
+		if(!import_marks_file_from_stream)
+			read_marks();
+	}
+
+	import_marks_file = make_fast_import_path(marks);
+	safe_create_leading_directories_const(import_marks_file);
+	import_marks_file_from_stream = from_stream;
+	import_marks_file_ignore_missing = ignore_missing;
+}
+
+static void option_date_format(const char *fmt)
+{
+	if (!strcmp(fmt, "raw"))
+		whenspec = WHENSPEC_RAW;
+	else if (!strcmp(fmt, "rfc2822"))
+		whenspec = WHENSPEC_RFC2822;
+	else if (!strcmp(fmt, "now"))
+		whenspec = WHENSPEC_NOW;
+	else
+		die("unknown --date-format argument %s", fmt);
+}
+
+static unsigned long ulong_arg(const char *option, const char *arg)
+{
+	char *endptr;
+	unsigned long rv = strtoul(arg, &endptr, 0);
+	if (strchr(arg, '-') || endptr == arg || *endptr)
+		die("%s: argument must be a non-negative integer", option);
+	return rv;
+}
+
+static void option_depth(const char *depth)
+{
+	max_depth = ulong_arg("--depth", depth);
+	if (max_depth > MAX_DEPTH)
+		die("--depth cannot exceed %u", MAX_DEPTH);
+}
+
+static void option_active_branches(const char *branches)
+{
+	max_active_branches = ulong_arg("--active-branches", branches);
+}
+
+static void option_export_marks(const char *marks)
+{
+	export_marks_file = make_fast_import_path(marks);
+	safe_create_leading_directories_const(export_marks_file);
+}
+
+static void option_cat_blob_fd(const char *fd)
+{
+	unsigned long n = ulong_arg("--cat-blob-fd", fd);
+	if (n > (unsigned long) INT_MAX)
+		die("--cat-blob-fd cannot exceed %d", INT_MAX);
+	cat_blob_fd = (int) n;
+}
+
+static void option_export_pack_edges(const char *edges)
+{
+	if (pack_edges)
+		fclose(pack_edges);
+	pack_edges = fopen(edges, "a");
+	if (!pack_edges)
+		die_errno("Cannot open '%s'", edges);
+}
+
+static int parse_one_option(const char *option)
+{
+	if (!prefixcmp(option, "max-pack-size=")) {
+		unsigned long v;
+		if (!git_parse_ulong(option + 14, &v))
+			return 0;
+		if (v < 8192) {
+			warning("max-pack-size is now in bytes, assuming --max-pack-size=%lum", v);
+			v *= 1024 * 1024;
+		} else if (v < 1024 * 1024) {
+			warning("minimum max-pack-size is 1 MiB");
+			v = 1024 * 1024;
+		}
+		max_packsize = v;
+	} else if (!prefixcmp(option, "big-file-threshold=")) {
+		unsigned long v;
+		if (!git_parse_ulong(option + 19, &v))
+			return 0;
+		big_file_threshold = v;
+	} else if (!prefixcmp(option, "depth=")) {
+		option_depth(option + 6);
+	} else if (!prefixcmp(option, "active-branches=")) {
+		option_active_branches(option + 16);
+	} else if (!prefixcmp(option, "export-pack-edges=")) {
+		option_export_pack_edges(option + 18);
+	} else if (!prefixcmp(option, "quiet")) {
+		show_stats = 0;
+	} else if (!prefixcmp(option, "stats")) {
+		show_stats = 1;
+	} else {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int parse_one_feature(const char *feature, int from_stream)
+{
+	if (!prefixcmp(feature, "date-format=")) {
+		option_date_format(feature + 12);
+	} else if (!prefixcmp(feature, "import-marks=")) {
+		option_import_marks(feature + 13, from_stream, 0);
+	} else if (!prefixcmp(feature, "import-marks-if-exists=")) {
+		option_import_marks(feature + strlen("import-marks-if-exists="),
+					from_stream, 1);
+	} else if (!prefixcmp(feature, "export-marks=")) {
+		option_export_marks(feature + 13);
+	} else if (!strcmp(feature, "cat-blob")) {
+		; /* Don't die - this feature is supported */
+	} else if (!prefixcmp(feature, "relative-marks")) {
+		relative_marks_paths = 1;
+	} else if (!prefixcmp(feature, "no-relative-marks")) {
+		relative_marks_paths = 0;
+	} else if (!prefixcmp(feature, "force")) {
+		force_update = 1;
+	} else if (!strcmp(feature, "notes") || !strcmp(feature, "ls")) {
+		; /* do nothing; we have the feature */
+	} else {
+		return 0;
+	}
+
+	return 1;
+}
+
+static void parse_feature(void)
+{
+	char *feature = command_buf.buf + 8;
+
+	if (seen_data_command)
+		die("Got feature command '%s' after data command", feature);
+
+	if (parse_one_feature(feature, 1))
+		return;
+
+	die("This version of fast-import does not support feature %s.", feature);
+}
+
+static void parse_option(void)
+{
+	char *option = command_buf.buf + 11;
+
+	if (seen_data_command)
+		die("Got option command '%s' after data command", option);
+
+	if (parse_one_option(option))
+		return;
+
+	die("This version of fast-import does not support option: %s", option);
+}
+
+static int git_pack_config(const char *k, const char *v, void *cb)
+{
+	if (!strcmp(k, "pack.depth")) {
+		max_depth = git_config_int(k, v);
+		if (max_depth > MAX_DEPTH)
+			max_depth = MAX_DEPTH;
+		return 0;
+	}
+	if (!strcmp(k, "pack.compression")) {
+		int level = git_config_int(k, v);
+		if (level == -1)
+			level = Z_DEFAULT_COMPRESSION;
+		else if (level < 0 || level > Z_BEST_COMPRESSION)
+			die("bad pack compression level %d", level);
+		pack_compression_level = level;
+		pack_compression_seen = 1;
+		return 0;
+	}
+	if (!strcmp(k, "pack.indexversion")) {
+		pack_idx_default_version = git_config_int(k, v);
+		if (pack_idx_default_version > 2)
+			die("bad pack.indexversion=%"PRIu32,
+			    pack_idx_default_version);
+		return 0;
+	}
+	if (!strcmp(k, "pack.packsizelimit")) {
+		max_packsize = git_config_ulong(k, v);
+		return 0;
+	}
+	if (!strcmp(k, "core.bigfilethreshold")) {
+		long n = git_config_int(k, v);
+		big_file_threshold = 0 < n ? n : 0;
+	}
+	return git_default_config(k, v, cb);
+}
+
+static const char fast_import_usage[] =
+"git fast-import [--date-format=<f>] [--max-pack-size=<n>] [--big-file-threshold=<n>] [--depth=<n>] [--active-branches=<n>] [--export-marks=<marks.file>]";
+
+static void parse_argv(void)
+{
+	unsigned int i;
+
+	for (i = 1; i < global_argc; i++) {
+		const char *a = global_argv[i];
+
+		if (*a != '-' || !strcmp(a, "--"))
+			break;
+
+		if (parse_one_option(a + 2))
+			continue;
+
+		if (parse_one_feature(a + 2, 0))
+			continue;
+
+		if (!prefixcmp(a + 2, "cat-blob-fd=")) {
+			option_cat_blob_fd(a + 2 + strlen("cat-blob-fd="));
+			continue;
+		}
+
+		die("unknown option %s", a);
+	}
+	if (i != global_argc)
+		usage(fast_import_usage);
+
+	seen_data_command = 1;
+	if (import_marks_file)
+		read_marks();
+}
+
+int main(int argc, const char **argv)
+{
+	unsigned int i;
+
+	git_extract_argv0_path(argv[0]);
+
+	if (argc == 2 && !strcmp(argv[1], "-h"))
+		usage(fast_import_usage);
+
+	setup_git_directory();
+	git_config(git_pack_config, NULL);
+	if (!pack_compression_seen && core_compression_seen)
+		pack_compression_level = core_compression_level;
+
+	alloc_objects(object_entry_alloc);
+	strbuf_init(&command_buf, 0);
+	atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*));
+	branch_table = xcalloc(branch_table_sz, sizeof(struct branch*));
+	avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
+	marks = pool_calloc(1, sizeof(struct mark_set));
+
+	global_argc = argc;
+	global_argv = argv;
+
+	rc_free = pool_alloc(cmd_save * sizeof(*rc_free));
+	for (i = 0; i < (cmd_save - 1); i++)
+		rc_free[i].next = &rc_free[i + 1];
+	rc_free[cmd_save - 1].next = NULL;
+
+	prepare_packed_git();
+	start_packfile();
+	set_die_routine(die_nicely);
+	set_checkpoint_signal();
+	while (read_next_command() != EOF) {
+		if (!strcmp("blob", command_buf.buf))
+			parse_new_blob();
+		else if (!prefixcmp(command_buf.buf, "ls "))
+			parse_ls(NULL);
+		else if (!prefixcmp(command_buf.buf, "commit "))
+			parse_new_commit();
+		else if (!prefixcmp(command_buf.buf, "tag "))
+			parse_new_tag();
+		else if (!prefixcmp(command_buf.buf, "reset "))
+			parse_reset_branch();
+		else if (!strcmp("checkpoint", command_buf.buf))
+			parse_checkpoint();
+		else if (!prefixcmp(command_buf.buf, "progress "))
+			parse_progress();
+		else if (!prefixcmp(command_buf.buf, "feature "))
+			parse_feature();
+		else if (!prefixcmp(command_buf.buf, "option git "))
+			parse_option();
+		else if (!prefixcmp(command_buf.buf, "option "))
+			/* ignore non-git options*/;
+		else
+			die("Unsupported command: %s", command_buf.buf);
+
+		if (checkpoint_requested)
+			checkpoint();
+	}
+
+	/* argv hasn't been parsed yet, do so */
+	if (!seen_data_command)
+		parse_argv();
+
+	end_packfile();
+
+	dump_branches();
+	dump_tags();
+	unkeep_all_packs();
+	dump_marks();
+
+	if (pack_edges)
+		fclose(pack_edges);
+
+	if (show_stats) {
+		uintmax_t total_count = 0, duplicate_count = 0;
+		for (i = 0; i < ARRAY_SIZE(object_count_by_type); i++)
+			total_count += object_count_by_type[i];
+		for (i = 0; i < ARRAY_SIZE(duplicate_count_by_type); i++)
+			duplicate_count += duplicate_count_by_type[i];
+
+		fprintf(stderr, "%s statistics:\n", argv[0]);
+		fprintf(stderr, "---------------------------------------------------------------------\n");
+		fprintf(stderr, "Alloc'd objects: %10" PRIuMAX "\n", alloc_count);
+		fprintf(stderr, "Total objects:   %10" PRIuMAX " (%10" PRIuMAX " duplicates                  )\n", total_count, duplicate_count);
+		fprintf(stderr, "      blobs  :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_BLOB], duplicate_count_by_type[OBJ_BLOB], delta_count_by_type[OBJ_BLOB]);
+		fprintf(stderr, "      trees  :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_TREE], duplicate_count_by_type[OBJ_TREE], delta_count_by_type[OBJ_TREE]);
+		fprintf(stderr, "      commits:   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_COMMIT], duplicate_count_by_type[OBJ_COMMIT], delta_count_by_type[OBJ_COMMIT]);
+		fprintf(stderr, "      tags   :   %10" PRIuMAX " (%10" PRIuMAX " duplicates %10" PRIuMAX " deltas)\n", object_count_by_type[OBJ_TAG], duplicate_count_by_type[OBJ_TAG], delta_count_by_type[OBJ_TAG]);
+		fprintf(stderr, "Total branches:  %10lu (%10lu loads     )\n", branch_count, branch_load_count);
+		fprintf(stderr, "      marks:     %10" PRIuMAX " (%10" PRIuMAX " unique    )\n", (((uintmax_t)1) << marks->shift) * 1024, marks_set_count);
+		fprintf(stderr, "      atoms:     %10u\n", atom_cnt);
+		fprintf(stderr, "Memory total:    %10" PRIuMAX " KiB\n", (total_allocd + alloc_count*sizeof(struct object_entry))/1024);
+		fprintf(stderr, "       pools:    %10lu KiB\n", (unsigned long)(total_allocd/1024));
+		fprintf(stderr, "     objects:    %10" PRIuMAX " KiB\n", (alloc_count*sizeof(struct object_entry))/1024);
+		fprintf(stderr, "---------------------------------------------------------------------\n");
+		pack_report();
+		fprintf(stderr, "---------------------------------------------------------------------\n");
+		fprintf(stderr, "\n");
+	}
+
+	return failure ? 1 : 0;
+}
diff --git a/git-submodule.sh b/git-submodule.sh
index b010a673097a9c..801aa8790cd1f1 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -17,6 +17,15 @@ OPTIONS_SPEC=
 . git-parse-remote
 require_work_tree
 
+# Restrict ourselves to a vanilla subset of protocols; the URLs
+# we get are under control of a remote repository, and we do not
+# want them kicking off arbitrary git-remote-* programs.
+#
+# If the user has already specified a set of allowed protocols,
+# we assume they know what they're doing and use that instead.
+: ${GIT_ALLOW_PROTOCOL=file:git:http:https:ssh}
+export GIT_ALLOW_PROTOCOL
+
 command=
 branch=
 force=
diff --git a/git-submodule.sh.orig b/git-submodule.sh.orig
new file mode 100755
index 00000000000000..b010a673097a9c
--- /dev/null
+++ b/git-submodule.sh.orig
@@ -0,0 +1,942 @@
+#!/bin/sh
+#
+# git-submodules.sh: add, init, update or list git submodules
+#
+# Copyright (c) 2007 Lars Hjemli
+
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="[--quiet] add [-b branch] [-f|--force] [--reference <repository>] [--] <repository> [<path>]
+   or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] init [--] [<path>...]
+   or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+   or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+   or: $dashless [--quiet] foreach [--recursive] <command>
+   or: $dashless [--quiet] sync [--] [<path>...]"
+OPTIONS_SPEC=
+. git-sh-setup
+. git-parse-remote
+require_work_tree
+
+command=
+branch=
+force=
+reference=
+cached=
+recursive=
+init=
+files=
+nofetch=
+update=
+prefix=
+
+# Resolve relative url by appending to parent's url
+resolve_relative_url ()
+{
+	remote=$(get_default_remote)
+	remoteurl=$(git config "remote.$remote.url") ||
+		die "remote ($remote) does not have a url defined in .git/config"
+	url="$1"
+	remoteurl=${remoteurl%/}
+	sep=/
+	while test -n "$url"
+	do
+		case "$url" in
+		../*)
+			url="${url#../}"
+			case "$remoteurl" in
+			*/*)
+				remoteurl="${remoteurl%/*}"
+				;;
+			*:*)
+				remoteurl="${remoteurl%:*}"
+				sep=:
+				;;
+			*)
+				die "cannot strip one component off url '$remoteurl'"
+				;;
+			esac
+			;;
+		./*)
+			url="${url#./}"
+			;;
+		*)
+			break;;
+		esac
+	done
+	echo "$remoteurl$sep${url%/}"
+}
+
+#
+# Get submodule info for registered submodules
+# $@ = path to limit submodule list
+#
+module_list()
+{
+	git ls-files --error-unmatch --stage -- "$@" |
+	perl -e '
+	my %unmerged = ();
+	my ($null_sha1) = ("0" x 40);
+	while (<STDIN>) {
+		chomp;
+		my ($mode, $sha1, $stage, $path) =
+			/^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
+		next unless $mode eq "160000";
+		if ($stage ne "0") {
+			if (!$unmerged{$path}++) {
+				print "$mode $null_sha1 U\t$path\n";
+			}
+			next;
+		}
+		print "$_\n";
+	}
+	'
+}
+
+#
+# Map submodule path to submodule name
+#
+# $1 = path
+#
+module_name()
+{
+	# Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
+	re=$(printf '%s\n' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
+	name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
+		sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
+       test -z "$name" &&
+       die "No submodule mapping found in .gitmodules for path '$path'"
+       echo "$name"
+}
+
+#
+# Clone a submodule
+#
+# Prior to calling, cmd_update checks that a possibly existing
+# path is not a git repository.
+# Likewise, cmd_add checks that path does not exist at all,
+# since it is the location of a new submodule.
+#
+module_clone()
+{
+	path=$1
+	url=$2
+	reference="$3"
+
+	if test -n "$reference"
+	then
+		git-clone "$reference" -n "$url" "$path"
+	else
+		git-clone -n "$url" "$path"
+	fi ||
+	die "Clone of '$url' into submodule path '$path' failed"
+}
+
+#
+# Add a new submodule to the working tree, .gitmodules and the index
+#
+# $@ = repo path
+#
+# optional branch is stored in global branch variable
+#
+cmd_add()
+{
+	# parse $args after "submodule ... add".
+	while test $# -ne 0
+	do
+		case "$1" in
+		-b | --branch)
+			case "$2" in '') usage ;; esac
+			branch=$2
+			shift
+			;;
+		-f | --force)
+			force=$1
+			;;
+		-q|--quiet)
+			GIT_QUIET=1
+			;;
+		--reference)
+			case "$2" in '') usage ;; esac
+			reference="--reference=$2"
+			shift
+			;;
+		--reference=*)
+			reference="$1"
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+
+	repo=$1
+	path=$2
+
+	if test -z "$path"; then
+		path=$(echo "$repo" |
+			sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
+	fi
+
+	if test -z "$repo" -o -z "$path"; then
+		usage
+	fi
+
+	# assure repo is absolute or relative to parent
+	case "$repo" in
+	./*|../*)
+		# dereference source url relative to parent's url
+		realrepo=$(resolve_relative_url "$repo") || exit
+		;;
+	*:*|/*)
+		# absolute url
+		realrepo=$repo
+		;;
+	*)
+		die "repo URL: '$repo' must be absolute or begin with ./|../"
+	;;
+	esac
+
+	# normalize path:
+	# multiple //; leading ./; /./; /../; trailing /
+	path=$(printf '%s/\n' "$path" |
+		sed -e '
+			s|//*|/|g
+			s|^\(\./\)*||
+			s|/\./|/|g
+			:start
+			s|\([^/]*\)/\.\./||
+			tstart
+			s|/*$||
+		')
+	git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
+	die "'$path' already exists in the index"
+
+	if test -z "$force" && ! git add --dry-run --ignore-missing "$path" > /dev/null 2>&1
+	then
+		echo >&2 "The following path is ignored by one of your .gitignore files:" &&
+		echo >&2 $path &&
+		echo >&2 "Use -f if you really want to add it."
+		exit 1
+	fi
+
+	# perhaps the path exists and is already a git repo, else clone it
+	if test -e "$path"
+	then
+		if test -d "$path"/.git -o -f "$path"/.git
+		then
+			echo "Adding existing repo at '$path' to the index"
+		else
+			die "'$path' already exists and is not a valid git repo"
+		fi
+
+		case "$repo" in
+		./*|../*)
+			url=$(resolve_relative_url "$repo") || exit
+		    ;;
+		*)
+			url="$repo"
+			;;
+		esac
+		git config submodule."$path".url "$url"
+	else
+
+		module_clone "$path" "$realrepo" "$reference" || exit
+		(
+			clear_local_git_env
+			cd "$path" &&
+			# ash fails to wordsplit ${branch:+-b "$branch"...}
+			case "$branch" in
+			'') git checkout -f -q ;;
+			?*) git checkout -f -q -B "$branch" "origin/$branch" ;;
+			esac
+		) || die "Unable to checkout submodule '$path'"
+	fi
+
+	git add $force "$path" ||
+	die "Failed to add submodule '$path'"
+
+	git config -f .gitmodules submodule."$path".path "$path" &&
+	git config -f .gitmodules submodule."$path".url "$repo" &&
+	git add --force .gitmodules ||
+	die "Failed to register submodule '$path'"
+}
+
+#
+# Execute an arbitrary command sequence in each checked out
+# submodule
+#
+# $@ = command to execute
+#
+cmd_foreach()
+{
+	# parse $args after "submodule ... foreach".
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			GIT_QUIET=1
+			;;
+		--recursive)
+			recursive=1
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+
+	toplevel=$(pwd)
+
+	module_list |
+	while read mode sha1 stage path
+	do
+		if test -e "$path"/.git
+		then
+			say "Entering '$prefix$path'"
+			name=$(module_name "$path")
+			(
+				prefix="$prefix$path/"
+				clear_local_git_env
+				cd "$path" &&
+				eval "$@" &&
+				if test -n "$recursive"
+				then
+					cmd_foreach "--recursive" "$@"
+				fi
+			) ||
+			die "Stopping at '$path'; script returned non-zero status."
+		fi
+	done
+}
+
+#
+# Register submodules in .git/config
+#
+# $@ = requested paths (default to all)
+#
+cmd_init()
+{
+	# parse $args after "submodule ... init".
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			GIT_QUIET=1
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+
+	module_list "$@" |
+	while read mode sha1 stage path
+	do
+		# Skip already registered paths
+		name=$(module_name "$path") || exit
+		url=$(git config submodule."$name".url)
+		test -z "$url" || continue
+
+		url=$(git config -f .gitmodules submodule."$name".url)
+		test -z "$url" &&
+		die "No url found for submodule path '$path' in .gitmodules"
+
+		# Possibly a url relative to parent
+		case "$url" in
+		./*|../*)
+			url=$(resolve_relative_url "$url") || exit
+			;;
+		esac
+
+		git config submodule."$name".url "$url" ||
+		die "Failed to register url for submodule path '$path'"
+
+		upd="$(git config -f .gitmodules submodule."$name".update)"
+		test -z "$upd" ||
+		git config submodule."$name".update "$upd" ||
+		die "Failed to register update mode for submodule path '$path'"
+
+		say "Submodule '$name' ($url) registered for path '$path'"
+	done
+}
+
+#
+# Update each submodule path to correct revision, using clone and checkout as needed
+#
+# $@ = requested paths (default to all)
+#
+cmd_update()
+{
+	# parse $args after "submodule ... update".
+	orig_flags=
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			GIT_QUIET=1
+			;;
+		-i|--init)
+			init=1
+			;;
+		-N|--no-fetch)
+			nofetch=1
+			;;
+		-r|--rebase)
+			update="rebase"
+			;;
+		--reference)
+			case "$2" in '') usage ;; esac
+			reference="--reference=$2"
+			orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
+			shift
+			;;
+		--reference=*)
+			reference="$1"
+			;;
+		-m|--merge)
+			update="merge"
+			;;
+		--recursive)
+			recursive=1
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
+		shift
+	done
+
+	if test -n "$init"
+	then
+		cmd_init "--" "$@" || return
+	fi
+
+	cloned_modules=
+	module_list "$@" |
+	while read mode sha1 stage path
+	do
+		if test "$stage" = U
+		then
+			echo >&2 "Skipping unmerged submodule $path"
+			continue
+		fi
+		name=$(module_name "$path") || exit
+		url=$(git config submodule."$name".url)
+		update_module=$(git config submodule."$name".update)
+		if test -z "$url"
+		then
+			# Only mention uninitialized submodules when its
+			# path have been specified
+			test "$#" != "0" &&
+			say "Submodule path '$path' not initialized" &&
+			say "Maybe you want to use 'update --init'?"
+			continue
+		fi
+
+		if ! test -d "$path"/.git -o -f "$path"/.git
+		then
+			module_clone "$path" "$url" "$reference"|| exit
+			cloned_modules="$cloned_modules;$name"
+			subsha1=
+		else
+			subsha1=$(clear_local_git_env; cd "$path" &&
+				git rev-parse --verify HEAD) ||
+			die "Unable to find current revision in submodule path '$path'"
+		fi
+
+		if ! test -z "$update"
+		then
+			update_module=$update
+		fi
+
+		if test "$subsha1" != "$sha1"
+		then
+			force=
+			if test -z "$subsha1"
+			then
+				force="-f"
+			fi
+
+			if test -z "$nofetch"
+			then
+				# Run fetch only if $sha1 isn't present or it
+				# is not reachable from a ref.
+				(clear_local_git_env; cd "$path" &&
+					((rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
+					 test -z "$rev") || git-fetch)) ||
+				die "Unable to fetch in submodule path '$path'"
+			fi
+
+			# Is this something we just cloned?
+			case ";$cloned_modules;" in
+			*";$name;"*)
+				# then there is no local change to integrate
+				update_module= ;;
+			esac
+
+			case "$update_module" in
+			rebase)
+				command="git rebase"
+				action="rebase"
+				msg="rebased onto"
+				;;
+			merge)
+				command="git merge"
+				action="merge"
+				msg="merged in"
+				;;
+			*)
+				command="git checkout $force -q"
+				action="checkout"
+				msg="checked out"
+				;;
+			esac
+
+			(clear_local_git_env; cd "$path" && $command "$sha1") ||
+			die "Unable to $action '$sha1' in submodule path '$path'"
+			say "Submodule path '$path': $msg '$sha1'"
+		fi
+
+		if test -n "$recursive"
+		then
+			(clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags") ||
+			die "Failed to recurse into submodule path '$path'"
+		fi
+	done
+}
+
+set_name_rev () {
+	revname=$( (
+		clear_local_git_env
+		cd "$1" && {
+			git describe "$2" 2>/dev/null ||
+			git describe --tags "$2" 2>/dev/null ||
+			git describe --contains "$2" 2>/dev/null ||
+			git describe --all --always "$2"
+		}
+	) )
+	test -z "$revname" || revname=" ($revname)"
+}
+#
+# Show commit summary for submodules in index or working tree
+#
+# If '--cached' is given, show summary between index and given commit,
+# or between working tree and given commit
+#
+# $@ = [commit (default 'HEAD'),] requested paths (default all)
+#
+cmd_summary() {
+	summary_limit=-1
+	for_status=
+	diff_cmd=diff-index
+
+	# parse $args after "submodule ... summary".
+	while test $# -ne 0
+	do
+		case "$1" in
+		--cached)
+			cached="$1"
+			;;
+		--files)
+			files="$1"
+			;;
+		--for-status)
+			for_status="$1"
+			;;
+		-n|--summary-limit)
+			if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
+			then
+				:
+			else
+				usage
+			fi
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		shift
+	done
+
+	test $summary_limit = 0 && return
+
+	if rev=$(git rev-parse -q --verify --default HEAD ${1+"$1"})
+	then
+		head=$rev
+		test $# = 0 || shift
+	elif test -z "$1" -o "$1" = "HEAD"
+	then
+		# before the first commit: compare with an empty tree
+		head=$(git hash-object -w -t tree --stdin </dev/null)
+		test -z "$1" || shift
+	else
+		head="HEAD"
+	fi
+
+	if [ -n "$files" ]
+	then
+		test -n "$cached" &&
+		die "--cached cannot be used with --files"
+		diff_cmd=diff-files
+		head=
+	fi
+
+	cd_to_toplevel
+	# Get modified modules cared by user
+	modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
+		sane_egrep '^:([0-7]* )?160000' |
+		while read mod_src mod_dst sha1_src sha1_dst status name
+		do
+			# Always show modules deleted or type-changed (blob<->module)
+			test $status = D -o $status = T && echo "$name" && continue
+			# Also show added or modified modules which are checked out
+			GIT_DIR="$name/.git" git-rev-parse --git-dir >/dev/null 2>&1 &&
+			echo "$name"
+		done
+	)
+
+	test -z "$modules" && return
+
+	git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
+	sane_egrep '^:([0-7]* )?160000' |
+	cut -c2- |
+	while read mod_src mod_dst sha1_src sha1_dst status name
+	do
+		if test -z "$cached" &&
+			test $sha1_dst = 0000000000000000000000000000000000000000
+		then
+			case "$mod_dst" in
+			160000)
+				sha1_dst=$(GIT_DIR="$name/.git" git rev-parse HEAD)
+				;;
+			100644 | 100755 | 120000)
+				sha1_dst=$(git hash-object $name)
+				;;
+			000000)
+				;; # removed
+			*)
+				# unexpected type
+				echo >&2 "unexpected mode $mod_dst"
+				continue ;;
+			esac
+		fi
+		missing_src=
+		missing_dst=
+
+		test $mod_src = 160000 &&
+		! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_src^0 >/dev/null &&
+		missing_src=t
+
+		test $mod_dst = 160000 &&
+		! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
+		missing_dst=t
+
+		total_commits=
+		case "$missing_src,$missing_dst" in
+		t,)
+			errmsg="  Warn: $name doesn't contain commit $sha1_src"
+			;;
+		,t)
+			errmsg="  Warn: $name doesn't contain commit $sha1_dst"
+			;;
+		t,t)
+			errmsg="  Warn: $name doesn't contain commits $sha1_src and $sha1_dst"
+			;;
+		*)
+			errmsg=
+			total_commits=$(
+			if test $mod_src = 160000 -a $mod_dst = 160000
+			then
+				range="$sha1_src...$sha1_dst"
+			elif test $mod_src = 160000
+			then
+				range=$sha1_src
+			else
+				range=$sha1_dst
+			fi
+			GIT_DIR="$name/.git" \
+			git rev-list --first-parent $range -- | wc -l
+			)
+			total_commits=" ($(($total_commits + 0)))"
+			;;
+		esac
+
+		sha1_abbr_src=$(echo $sha1_src | cut -c1-7)
+		sha1_abbr_dst=$(echo $sha1_dst | cut -c1-7)
+		if test $status = T
+		then
+			if test $mod_dst = 160000
+			then
+				echo "* $name $sha1_abbr_src(blob)->$sha1_abbr_dst(submodule)$total_commits:"
+			else
+				echo "* $name $sha1_abbr_src(submodule)->$sha1_abbr_dst(blob)$total_commits:"
+			fi
+		else
+			echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
+		fi
+		if test -n "$errmsg"
+		then
+			# Don't give error msg for modification whose dst is not submodule
+			# i.e. deleted or changed to blob
+			test $mod_dst = 160000 && echo "$errmsg"
+		else
+			if test $mod_src = 160000 -a $mod_dst = 160000
+			then
+				limit=
+				test $summary_limit -gt 0 && limit="-$summary_limit"
+				GIT_DIR="$name/.git" \
+				git log $limit --pretty='format:  %m %s' \
+				--first-parent $sha1_src...$sha1_dst
+			elif test $mod_dst = 160000
+			then
+				GIT_DIR="$name/.git" \
+				git log --pretty='format:  > %s' -1 $sha1_dst
+			else
+				GIT_DIR="$name/.git" \
+				git log --pretty='format:  < %s' -1 $sha1_src
+			fi
+			echo
+		fi
+		echo
+	done |
+	if test -n "$for_status"; then
+		if [ -n "$files" ]; then
+			echo "# Submodules changed but not updated:"
+		else
+			echo "# Submodule changes to be committed:"
+		fi
+		echo "#"
+		sed -e 's|^|# |' -e 's|^# $|#|'
+	else
+		cat
+	fi
+}
+#
+# List all submodules, prefixed with:
+#  - submodule not initialized
+#  + different revision checked out
+#
+# If --cached was specified the revision in the index will be printed
+# instead of the currently checked out revision.
+#
+# $@ = requested paths (default to all)
+#
+cmd_status()
+{
+	# parse $args after "submodule ... status".
+	orig_flags=
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			GIT_QUIET=1
+			;;
+		--cached)
+			cached=1
+			;;
+		--recursive)
+			recursive=1
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+		orig_flags="$orig_flags $(git rev-parse --sq-quote "$1")"
+		shift
+	done
+
+	module_list "$@" |
+	while read mode sha1 stage path
+	do
+		name=$(module_name "$path") || exit
+		url=$(git config submodule."$name".url)
+		displaypath="$prefix$path"
+		if test "$stage" = U
+		then
+			say "U$sha1 $displaypath"
+			continue
+		fi
+		if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
+		then
+			say "-$sha1 $displaypath"
+			continue;
+		fi
+		set_name_rev "$path" "$sha1"
+		if git diff-files --ignore-submodules=dirty --quiet -- "$path"
+		then
+			say " $sha1 $displaypath$revname"
+		else
+			if test -z "$cached"
+			then
+				sha1=$(clear_local_git_env; cd "$path" && git rev-parse --verify HEAD)
+				set_name_rev "$path" "$sha1"
+			fi
+			say "+$sha1 $displaypath$revname"
+		fi
+
+		if test -n "$recursive"
+		then
+			(
+				prefix="$displaypath/"
+				clear_local_git_env
+				cd "$path" &&
+				eval cmd_status "$orig_args"
+			) ||
+			die "Failed to recurse into submodule path '$path'"
+		fi
+	done
+}
+#
+# Sync remote urls for submodules
+# This makes the value for remote.$remote.url match the value
+# specified in .gitmodules.
+#
+cmd_sync()
+{
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			GIT_QUIET=1
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+	done
+	cd_to_toplevel
+	module_list "$@" |
+	while read mode sha1 stage path
+	do
+		name=$(module_name "$path")
+		url=$(git config -f .gitmodules --get submodule."$name".url)
+
+		# Possibly a url relative to parent
+		case "$url" in
+		./*|../*)
+			url=$(resolve_relative_url "$url") || exit
+			;;
+		esac
+
+		say "Synchronizing submodule url for '$name'"
+		git config submodule."$name".url "$url"
+
+		if test -e "$path"/.git
+		then
+		(
+			clear_local_git_env
+			cd "$path"
+			remote=$(get_default_remote)
+			git config remote."$remote".url "$url"
+		)
+		fi
+	done
+}
+
+# This loop parses the command line arguments to find the
+# subcommand name to dispatch.  Parsing of the subcommand specific
+# options are primarily done by the subcommand implementations.
+# Subcommand specific options such as --branch and --cached are
+# parsed here as well, for backward compatibility.
+
+while test $# != 0 && test -z "$command"
+do
+	case "$1" in
+	add | foreach | init | update | status | summary | sync)
+		command=$1
+		;;
+	-q|--quiet)
+		GIT_QUIET=1
+		;;
+	-b|--branch)
+		case "$2" in
+		'')
+			usage
+			;;
+		esac
+		branch="$2"; shift
+		;;
+	--cached)
+		cached="$1"
+		;;
+	--)
+		break
+		;;
+	-*)
+		usage
+		;;
+	*)
+		break
+		;;
+	esac
+	shift
+done
+
+# No command word defaults to "status"
+test -n "$command" || command=status
+
+# "-b branch" is accepted only by "add"
+if test -n "$branch" && test "$command" != add
+then
+	usage
+fi
+
+# "--cached" is accepted only by "status" and "summary"
+if test -n "$cached" && test "$command" != status -a "$command" != summary
+then
+	usage
+fi
+
+"cmd_$command" "$@"
diff --git a/revision.c b/revision.c
index 0f38364cf3f81a..f1ce4760d9c848 100644
--- a/revision.c
+++ b/revision.c
@@ -29,7 +29,7 @@ char *path_name(const struct name_path *path, const char *name)
 	}
 	n = xmalloc(len);
 	m = n + len - (nlen + 1);
-	strcpy(m, name);
+	memcpy(m, name, nlen + 1);
 	for (p = path; p; p = p->up) {
 		if (p->elem_len) {
 			m -= p->elem_len + 1;
diff --git a/revision.c.orig b/revision.c.orig
new file mode 100644
index 00000000000000..0f38364cf3f81a
--- /dev/null
+++ b/revision.c.orig
@@ -0,0 +1,2324 @@
+#include "cache.h"
+#include "tag.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "diff.h"
+#include "refs.h"
+#include "revision.h"
+#include "graph.h"
+#include "grep.h"
+#include "reflog-walk.h"
+#include "patch-ids.h"
+#include "decorate.h"
+#include "log-tree.h"
+#include "string-list.h"
+
+volatile show_early_output_fn_t show_early_output;
+
+char *path_name(const struct name_path *path, const char *name)
+{
+	const struct name_path *p;
+	char *n, *m;
+	int nlen = strlen(name);
+	int len = nlen + 1;
+
+	for (p = path; p; p = p->up) {
+		if (p->elem_len)
+			len += p->elem_len + 1;
+	}
+	n = xmalloc(len);
+	m = n + len - (nlen + 1);
+	strcpy(m, name);
+	for (p = path; p; p = p->up) {
+		if (p->elem_len) {
+			m -= p->elem_len + 1;
+			memcpy(m, p->elem, p->elem_len);
+			m[p->elem_len] = '/';
+		}
+	}
+	return n;
+}
+
+void add_object(struct object *obj,
+		struct object_array *p,
+		struct name_path *path,
+		const char *name)
+{
+	add_object_array(obj, path_name(path, name), p);
+}
+
+static void mark_blob_uninteresting(struct blob *blob)
+{
+	if (!blob)
+		return;
+	if (blob->object.flags & UNINTERESTING)
+		return;
+	blob->object.flags |= UNINTERESTING;
+}
+
+void mark_tree_uninteresting(struct tree *tree)
+{
+	struct tree_desc desc;
+	struct name_entry entry;
+	struct object *obj = &tree->object;
+
+	if (!tree)
+		return;
+	if (obj->flags & UNINTERESTING)
+		return;
+	obj->flags |= UNINTERESTING;
+	if (!has_sha1_file(obj->sha1))
+		return;
+	if (parse_tree(tree) < 0)
+		die("bad tree %s", sha1_to_hex(obj->sha1));
+
+	init_tree_desc(&desc, tree->buffer, tree->size);
+	while (tree_entry(&desc, &entry)) {
+		switch (object_type(entry.mode)) {
+		case OBJ_TREE:
+			mark_tree_uninteresting(lookup_tree(entry.sha1));
+			break;
+		case OBJ_BLOB:
+			mark_blob_uninteresting(lookup_blob(entry.sha1));
+			break;
+		default:
+			/* Subproject commit - not in this repository */
+			break;
+		}
+	}
+
+	/*
+	 * We don't care about the tree any more
+	 * after it has been marked uninteresting.
+	 */
+	free(tree->buffer);
+	tree->buffer = NULL;
+}
+
+void mark_parents_uninteresting(struct commit *commit)
+{
+	struct commit_list *parents = commit->parents;
+
+	while (parents) {
+		struct commit *commit = parents->item;
+		if (!(commit->object.flags & UNINTERESTING)) {
+			commit->object.flags |= UNINTERESTING;
+
+			/*
+			 * Normally we haven't parsed the parent
+			 * yet, so we won't have a parent of a parent
+			 * here. However, it may turn out that we've
+			 * reached this commit some other way (where it
+			 * wasn't uninteresting), in which case we need
+			 * to mark its parents recursively too..
+			 */
+			if (commit->parents)
+				mark_parents_uninteresting(commit);
+		}
+
+		/*
+		 * A missing commit is ok iff its parent is marked
+		 * uninteresting.
+		 *
+		 * We just mark such a thing parsed, so that when
+		 * it is popped next time around, we won't be trying
+		 * to parse it and get an error.
+		 */
+		if (!has_sha1_file(commit->object.sha1))
+			commit->object.parsed = 1;
+		parents = parents->next;
+	}
+}
+
+static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
+{
+	if (revs->no_walk && (obj->flags & UNINTERESTING))
+		revs->no_walk = 0;
+	if (revs->reflog_info && obj->type == OBJ_COMMIT) {
+		struct strbuf buf = STRBUF_INIT;
+		int len = interpret_branch_name(name, &buf);
+		int st;
+
+		if (0 < len && name[len] && buf.len)
+			strbuf_addstr(&buf, name + len);
+		st = add_reflog_for_walk(revs->reflog_info,
+					 (struct commit *)obj,
+					 buf.buf[0] ? buf.buf: name);
+		strbuf_release(&buf);
+		if (st)
+			return;
+	}
+	add_object_array_with_mode(obj, name, &revs->pending, mode);
+}
+
+void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+{
+	add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
+}
+
+void add_head_to_pending(struct rev_info *revs)
+{
+	unsigned char sha1[20];
+	struct object *obj;
+	if (get_sha1("HEAD", sha1))
+		return;
+	obj = parse_object(sha1);
+	if (!obj)
+		return;
+	add_pending_object(revs, obj, "HEAD");
+}
+
+static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
+{
+	struct object *object;
+
+	object = parse_object(sha1);
+	if (!object)
+		die("bad object %s", name);
+	object->flags |= flags;
+	return object;
+}
+
+static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
+{
+	unsigned long flags = object->flags;
+
+	/*
+	 * Tag object? Look what it points to..
+	 */
+	while (object->type == OBJ_TAG) {
+		struct tag *tag = (struct tag *) object;
+		if (revs->tag_objects && !(flags & UNINTERESTING))
+			add_pending_object(revs, object, tag->tag);
+		if (!tag->tagged)
+			die("bad tag");
+		object = parse_object(tag->tagged->sha1);
+		if (!object) {
+			if (flags & UNINTERESTING)
+				return NULL;
+			die("bad object %s", sha1_to_hex(tag->tagged->sha1));
+		}
+	}
+
+	/*
+	 * Commit object? Just return it, we'll do all the complex
+	 * reachability crud.
+	 */
+	if (object->type == OBJ_COMMIT) {
+		struct commit *commit = (struct commit *)object;
+		if (parse_commit(commit) < 0)
+			die("unable to parse commit %s", name);
+		if (flags & UNINTERESTING) {
+			commit->object.flags |= UNINTERESTING;
+			mark_parents_uninteresting(commit);
+			revs->limited = 1;
+		}
+		if (revs->show_source && !commit->util)
+			commit->util = (void *) name;
+		return commit;
+	}
+
+	/*
+	 * Tree object? Either mark it uninteresting, or add it
+	 * to the list of objects to look at later..
+	 */
+	if (object->type == OBJ_TREE) {
+		struct tree *tree = (struct tree *)object;
+		if (!revs->tree_objects)
+			return NULL;
+		if (flags & UNINTERESTING) {
+			mark_tree_uninteresting(tree);
+			return NULL;
+		}
+		add_pending_object(revs, object, "");
+		return NULL;
+	}
+
+	/*
+	 * Blob object? You know the drill by now..
+	 */
+	if (object->type == OBJ_BLOB) {
+		struct blob *blob = (struct blob *)object;
+		if (!revs->blob_objects)
+			return NULL;
+		if (flags & UNINTERESTING) {
+			mark_blob_uninteresting(blob);
+			return NULL;
+		}
+		add_pending_object(revs, object, "");
+		return NULL;
+	}
+	die("%s is unknown object", name);
+}
+
+static int everybody_uninteresting(struct commit_list *orig)
+{
+	struct commit_list *list = orig;
+	while (list) {
+		struct commit *commit = list->item;
+		list = list->next;
+		if (commit->object.flags & UNINTERESTING)
+			continue;
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * The goal is to get REV_TREE_NEW as the result only if the
+ * diff consists of all '+' (and no other changes), REV_TREE_OLD
+ * if the whole diff is removal of old data, and otherwise
+ * REV_TREE_DIFFERENT (of course if the trees are the same we
+ * want REV_TREE_SAME).
+ * That means that once we get to REV_TREE_DIFFERENT, we do not
+ * have to look any further.
+ */
+static int tree_difference = REV_TREE_SAME;
+
+static void file_add_remove(struct diff_options *options,
+		    int addremove, unsigned mode,
+		    const unsigned char *sha1,
+		    const char *fullpath, unsigned dirty_submodule)
+{
+	int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
+
+	tree_difference |= diff;
+	if (tree_difference == REV_TREE_DIFFERENT)
+		DIFF_OPT_SET(options, HAS_CHANGES);
+}
+
+static void file_change(struct diff_options *options,
+		 unsigned old_mode, unsigned new_mode,
+		 const unsigned char *old_sha1,
+		 const unsigned char *new_sha1,
+		 const char *fullpath,
+		 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
+{
+	tree_difference = REV_TREE_DIFFERENT;
+	DIFF_OPT_SET(options, HAS_CHANGES);
+}
+
+static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct commit *commit)
+{
+	struct tree *t1 = parent->tree;
+	struct tree *t2 = commit->tree;
+
+	if (!t1)
+		return REV_TREE_NEW;
+	if (!t2)
+		return REV_TREE_OLD;
+
+	if (revs->simplify_by_decoration) {
+		/*
+		 * If we are simplifying by decoration, then the commit
+		 * is worth showing if it has a tag pointing at it.
+		 */
+		if (lookup_decoration(&name_decoration, &commit->object))
+			return REV_TREE_DIFFERENT;
+		/*
+		 * A commit that is not pointed by a tag is uninteresting
+		 * if we are not limited by path.  This means that you will
+		 * see the usual "commits that touch the paths" plus any
+		 * tagged commit by specifying both --simplify-by-decoration
+		 * and pathspec.
+		 */
+		if (!revs->prune_data.nr)
+			return REV_TREE_SAME;
+	}
+
+	tree_difference = REV_TREE_SAME;
+	DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+	if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
+			   &revs->pruning) < 0)
+		return REV_TREE_DIFFERENT;
+	return tree_difference;
+}
+
+static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
+{
+	int retval;
+	void *tree;
+	unsigned long size;
+	struct tree_desc empty, real;
+	struct tree *t1 = commit->tree;
+
+	if (!t1)
+		return 0;
+
+	tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
+	if (!tree)
+		return 0;
+	init_tree_desc(&real, tree, size);
+	init_tree_desc(&empty, "", 0);
+
+	tree_difference = REV_TREE_SAME;
+	DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
+	retval = diff_tree(&empty, &real, "", &revs->pruning);
+	free(tree);
+
+	return retval >= 0 && (tree_difference == REV_TREE_SAME);
+}
+
+static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+	struct commit_list **pp, *parent;
+	int tree_changed = 0, tree_same = 0;
+
+	/*
+	 * If we don't do pruning, everything is interesting
+	 */
+	if (!revs->prune)
+		return;
+
+	if (!commit->tree)
+		return;
+
+	if (!commit->parents) {
+		if (rev_same_tree_as_empty(revs, commit))
+			commit->object.flags |= TREESAME;
+		return;
+	}
+
+	/*
+	 * Normal non-merge commit? If we don't want to make the
+	 * history dense, we consider it always to be a change..
+	 */
+	if (!revs->dense && !commit->parents->next)
+		return;
+
+	pp = &commit->parents;
+	while ((parent = *pp) != NULL) {
+		struct commit *p = parent->item;
+
+		if (parse_commit(p) < 0)
+			die("cannot simplify commit %s (because of %s)",
+			    sha1_to_hex(commit->object.sha1),
+			    sha1_to_hex(p->object.sha1));
+		switch (rev_compare_tree(revs, p, commit)) {
+		case REV_TREE_SAME:
+			tree_same = 1;
+			if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
+				/* Even if a merge with an uninteresting
+				 * side branch brought the entire change
+				 * we are interested in, we do not want
+				 * to lose the other branches of this
+				 * merge, so we just keep going.
+				 */
+				pp = &parent->next;
+				continue;
+			}
+			parent->next = NULL;
+			commit->parents = parent;
+			commit->object.flags |= TREESAME;
+			return;
+
+		case REV_TREE_NEW:
+			if (revs->remove_empty_trees &&
+			    rev_same_tree_as_empty(revs, p)) {
+				/* We are adding all the specified
+				 * paths from this parent, so the
+				 * history beyond this parent is not
+				 * interesting.  Remove its parents
+				 * (they are grandparents for us).
+				 * IOW, we pretend this parent is a
+				 * "root" commit.
+				 */
+				if (parse_commit(p) < 0)
+					die("cannot simplify commit %s (invalid %s)",
+					    sha1_to_hex(commit->object.sha1),
+					    sha1_to_hex(p->object.sha1));
+				p->parents = NULL;
+			}
+		/* fallthrough */
+		case REV_TREE_OLD:
+		case REV_TREE_DIFFERENT:
+			tree_changed = 1;
+			pp = &parent->next;
+			continue;
+		}
+		die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
+	}
+	if (tree_changed && !tree_same)
+		return;
+	commit->object.flags |= TREESAME;
+}
+
+static void commit_list_insert_by_date_cached(struct commit *p, struct commit_list **head,
+		    struct commit_list *cached_base, struct commit_list **cache)
+{
+	struct commit_list *new_entry;
+
+	if (cached_base && p->date < cached_base->item->date)
+		new_entry = commit_list_insert_by_date(p, &cached_base->next);
+	else
+		new_entry = commit_list_insert_by_date(p, head);
+
+	if (cache && (!*cache || p->date < (*cache)->item->date))
+		*cache = new_entry;
+}
+
+static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
+		    struct commit_list **list, struct commit_list **cache_ptr)
+{
+	struct commit_list *parent = commit->parents;
+	unsigned left_flag;
+	struct commit_list *cached_base = cache_ptr ? *cache_ptr : NULL;
+
+	if (commit->object.flags & ADDED)
+		return 0;
+	commit->object.flags |= ADDED;
+
+	/*
+	 * If the commit is uninteresting, don't try to
+	 * prune parents - we want the maximal uninteresting
+	 * set.
+	 *
+	 * Normally we haven't parsed the parent
+	 * yet, so we won't have a parent of a parent
+	 * here. However, it may turn out that we've
+	 * reached this commit some other way (where it
+	 * wasn't uninteresting), in which case we need
+	 * to mark its parents recursively too..
+	 */
+	if (commit->object.flags & UNINTERESTING) {
+		while (parent) {
+			struct commit *p = parent->item;
+			parent = parent->next;
+			if (p)
+				p->object.flags |= UNINTERESTING;
+			if (parse_commit(p) < 0)
+				continue;
+			if (p->parents)
+				mark_parents_uninteresting(p);
+			if (p->object.flags & SEEN)
+				continue;
+			p->object.flags |= SEEN;
+			commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
+		}
+		return 0;
+	}
+
+	/*
+	 * Ok, the commit wasn't uninteresting. Try to
+	 * simplify the commit history and find the parent
+	 * that has no differences in the path set if one exists.
+	 */
+	try_to_simplify_commit(revs, commit);
+
+	if (revs->no_walk)
+		return 0;
+
+	left_flag = (commit->object.flags & SYMMETRIC_LEFT);
+
+	for (parent = commit->parents; parent; parent = parent->next) {
+		struct commit *p = parent->item;
+
+		if (parse_commit(p) < 0)
+			return -1;
+		if (revs->show_source && !p->util)
+			p->util = commit->util;
+		p->object.flags |= left_flag;
+		if (!(p->object.flags & SEEN)) {
+			p->object.flags |= SEEN;
+			commit_list_insert_by_date_cached(p, list, cached_base, cache_ptr);
+		}
+		if (revs->first_parent_only)
+			break;
+	}
+	return 0;
+}
+
+static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
+{
+	struct commit_list *p;
+	int left_count = 0, right_count = 0;
+	int left_first;
+	struct patch_ids ids;
+	unsigned cherry_flag;
+
+	/* First count the commits on the left and on the right */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		unsigned flags = commit->object.flags;
+		if (flags & BOUNDARY)
+			;
+		else if (flags & SYMMETRIC_LEFT)
+			left_count++;
+		else
+			right_count++;
+	}
+
+	if (!left_count || !right_count)
+		return;
+
+	left_first = left_count < right_count;
+	init_patch_ids(&ids);
+	ids.diffopts.pathspec = revs->diffopt.pathspec;
+
+	/* Compute patch-ids for one side */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		unsigned flags = commit->object.flags;
+
+		if (flags & BOUNDARY)
+			continue;
+		/*
+		 * If we have fewer left, left_first is set and we omit
+		 * commits on the right branch in this loop.  If we have
+		 * fewer right, we skip the left ones.
+		 */
+		if (left_first != !!(flags & SYMMETRIC_LEFT))
+			continue;
+		commit->util = add_commit_patch_id(commit, &ids);
+	}
+
+	/* either cherry_mark or cherry_pick are true */
+	cherry_flag = revs->cherry_mark ? PATCHSAME : SHOWN;
+
+	/* Check the other side */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		struct patch_id *id;
+		unsigned flags = commit->object.flags;
+
+		if (flags & BOUNDARY)
+			continue;
+		/*
+		 * If we have fewer left, left_first is set and we omit
+		 * commits on the left branch in this loop.
+		 */
+		if (left_first == !!(flags & SYMMETRIC_LEFT))
+			continue;
+
+		/*
+		 * Have we seen the same patch id?
+		 */
+		id = has_commit_patch_id(commit, &ids);
+		if (!id)
+			continue;
+		id->seen = 1;
+		commit->object.flags |= cherry_flag;
+	}
+
+	/* Now check the original side for seen ones */
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+		struct patch_id *ent;
+
+		ent = commit->util;
+		if (!ent)
+			continue;
+		if (ent->seen)
+			commit->object.flags |= cherry_flag;
+		commit->util = NULL;
+	}
+
+	free_patch_ids(&ids);
+}
+
+/* How many extra uninteresting commits we want to see.. */
+#define SLOP 5
+
+static int still_interesting(struct commit_list *src, unsigned long date, int slop)
+{
+	/*
+	 * No source list at all? We're definitely done..
+	 */
+	if (!src)
+		return 0;
+
+	/*
+	 * Does the destination list contain entries with a date
+	 * before the source list? Definitely _not_ done.
+	 */
+	if (date < src->item->date)
+		return SLOP;
+
+	/*
+	 * Does the source list still have interesting commits in
+	 * it? Definitely not done..
+	 */
+	if (!everybody_uninteresting(src))
+		return SLOP;
+
+	/* Ok, we're closing in.. */
+	return slop-1;
+}
+
+/*
+ * "rev-list --ancestry-path A..B" computes commits that are ancestors
+ * of B but not ancestors of A but further limits the result to those
+ * that are descendants of A.  This takes the list of bottom commits and
+ * the result of "A..B" without --ancestry-path, and limits the latter
+ * further to the ones that can reach one of the commits in "bottom".
+ */
+static void limit_to_ancestry(struct commit_list *bottom, struct commit_list *list)
+{
+	struct commit_list *p;
+	struct commit_list *rlist = NULL;
+	int made_progress;
+
+	/*
+	 * Reverse the list so that it will be likely that we would
+	 * process parents before children.
+	 */
+	for (p = list; p; p = p->next)
+		commit_list_insert(p->item, &rlist);
+
+	for (p = bottom; p; p = p->next)
+		p->item->object.flags |= TMP_MARK;
+
+	/*
+	 * Mark the ones that can reach bottom commits in "list",
+	 * in a bottom-up fashion.
+	 */
+	do {
+		made_progress = 0;
+		for (p = rlist; p; p = p->next) {
+			struct commit *c = p->item;
+			struct commit_list *parents;
+			if (c->object.flags & (TMP_MARK | UNINTERESTING))
+				continue;
+			for (parents = c->parents;
+			     parents;
+			     parents = parents->next) {
+				if (!(parents->item->object.flags & TMP_MARK))
+					continue;
+				c->object.flags |= TMP_MARK;
+				made_progress = 1;
+				break;
+			}
+		}
+	} while (made_progress);
+
+	/*
+	 * NEEDSWORK: decide if we want to remove parents that are
+	 * not marked with TMP_MARK from commit->parents for commits
+	 * in the resulting list.  We may not want to do that, though.
+	 */
+
+	/*
+	 * The ones that are not marked with TMP_MARK are uninteresting
+	 */
+	for (p = list; p; p = p->next) {
+		struct commit *c = p->item;
+		if (c->object.flags & TMP_MARK)
+			continue;
+		c->object.flags |= UNINTERESTING;
+	}
+
+	/* We are done with the TMP_MARK */
+	for (p = list; p; p = p->next)
+		p->item->object.flags &= ~TMP_MARK;
+	for (p = bottom; p; p = p->next)
+		p->item->object.flags &= ~TMP_MARK;
+	free_commit_list(rlist);
+}
+
+/*
+ * Before walking the history, keep the set of "negative" refs the
+ * caller has asked to exclude.
+ *
+ * This is used to compute "rev-list --ancestry-path A..B", as we need
+ * to filter the result of "A..B" further to the ones that can actually
+ * reach A.
+ */
+static struct commit_list *collect_bottom_commits(struct commit_list *list)
+{
+	struct commit_list *elem, *bottom = NULL;
+	for (elem = list; elem; elem = elem->next)
+		if (elem->item->object.flags & UNINTERESTING)
+			commit_list_insert(elem->item, &bottom);
+	return bottom;
+}
+
+/* Assumes either left_only or right_only is set */
+static void limit_left_right(struct commit_list *list, struct rev_info *revs)
+{
+	struct commit_list *p;
+
+	for (p = list; p; p = p->next) {
+		struct commit *commit = p->item;
+
+		if (revs->right_only) {
+			if (commit->object.flags & SYMMETRIC_LEFT)
+				commit->object.flags |= SHOWN;
+		} else	/* revs->left_only is set */
+			if (!(commit->object.flags & SYMMETRIC_LEFT))
+				commit->object.flags |= SHOWN;
+	}
+}
+
+static int limit_list(struct rev_info *revs)
+{
+	int slop = SLOP;
+	unsigned long date = ~0ul;
+	struct commit_list *list = revs->commits;
+	struct commit_list *newlist = NULL;
+	struct commit_list **p = &newlist;
+	struct commit_list *bottom = NULL;
+
+	if (revs->ancestry_path) {
+		bottom = collect_bottom_commits(list);
+		if (!bottom)
+			die("--ancestry-path given but there are no bottom commits");
+	}
+
+	while (list) {
+		struct commit_list *entry = list;
+		struct commit *commit = list->item;
+		struct object *obj = &commit->object;
+		show_early_output_fn_t show;
+
+		list = list->next;
+		free(entry);
+
+		if (revs->max_age != -1 && (commit->date < revs->max_age))
+			obj->flags |= UNINTERESTING;
+		if (add_parents_to_list(revs, commit, &list, NULL) < 0)
+			return -1;
+		if (obj->flags & UNINTERESTING) {
+			mark_parents_uninteresting(commit);
+			if (revs->show_all)
+				p = &commit_list_insert(commit, p)->next;
+			slop = still_interesting(list, date, slop);
+			if (slop)
+				continue;
+			/* If showing all, add the whole pending list to the end */
+			if (revs->show_all)
+				*p = list;
+			break;
+		}
+		if (revs->min_age != -1 && (commit->date > revs->min_age))
+			continue;
+		date = commit->date;
+		p = &commit_list_insert(commit, p)->next;
+
+		show = show_early_output;
+		if (!show)
+			continue;
+
+		show(revs, newlist);
+		show_early_output = NULL;
+	}
+	if (revs->cherry_pick || revs->cherry_mark)
+		cherry_pick_list(newlist, revs);
+
+	if (revs->left_only || revs->right_only)
+		limit_left_right(newlist, revs);
+
+	if (bottom) {
+		limit_to_ancestry(bottom, newlist);
+		free_commit_list(bottom);
+	}
+
+	revs->commits = newlist;
+	return 0;
+}
+
+struct all_refs_cb {
+	int all_flags;
+	int warned_bad_reflog;
+	struct rev_info *all_revs;
+	const char *name_for_errormsg;
+};
+
+static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct all_refs_cb *cb = cb_data;
+	struct object *object = get_reference(cb->all_revs, path, sha1,
+					      cb->all_flags);
+	add_pending_object(cb->all_revs, object, path);
+	return 0;
+}
+
+static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
+	unsigned flags)
+{
+	cb->all_revs = revs;
+	cb->all_flags = flags;
+}
+
+static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
+		int (*for_each)(const char *, each_ref_fn, void *))
+{
+	struct all_refs_cb cb;
+	init_all_refs_cb(&cb, revs, flags);
+	for_each(submodule, handle_one_ref, &cb);
+}
+
+static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
+{
+	struct all_refs_cb *cb = cb_data;
+	if (!is_null_sha1(sha1)) {
+		struct object *o = parse_object(sha1);
+		if (o) {
+			o->flags |= cb->all_flags;
+			add_pending_object(cb->all_revs, o, "");
+		}
+		else if (!cb->warned_bad_reflog) {
+			warning("reflog of '%s' references pruned commits",
+				cb->name_for_errormsg);
+			cb->warned_bad_reflog = 1;
+		}
+	}
+}
+
+static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+		const char *email, unsigned long timestamp, int tz,
+		const char *message, void *cb_data)
+{
+	handle_one_reflog_commit(osha1, cb_data);
+	handle_one_reflog_commit(nsha1, cb_data);
+	return 0;
+}
+
+static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct all_refs_cb *cb = cb_data;
+	cb->warned_bad_reflog = 0;
+	cb->name_for_errormsg = path;
+	for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
+	return 0;
+}
+
+static void handle_reflog(struct rev_info *revs, unsigned flags)
+{
+	struct all_refs_cb cb;
+	cb.all_revs = revs;
+	cb.all_flags = flags;
+	for_each_reflog(handle_one_reflog, &cb);
+}
+
+static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
+{
+	unsigned char sha1[20];
+	struct object *it;
+	struct commit *commit;
+	struct commit_list *parents;
+
+	if (*arg == '^') {
+		flags ^= UNINTERESTING;
+		arg++;
+	}
+	if (get_sha1(arg, sha1))
+		return 0;
+	while (1) {
+		it = get_reference(revs, arg, sha1, 0);
+		if (it->type != OBJ_TAG)
+			break;
+		if (!((struct tag*)it)->tagged)
+			return 0;
+		hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
+	}
+	if (it->type != OBJ_COMMIT)
+		return 0;
+	commit = (struct commit *)it;
+	for (parents = commit->parents; parents; parents = parents->next) {
+		it = &parents->item->object;
+		it->flags |= flags;
+		add_pending_object(revs, it, arg);
+	}
+	return 1;
+}
+
+void init_revisions(struct rev_info *revs, const char *prefix)
+{
+	memset(revs, 0, sizeof(*revs));
+
+	revs->abbrev = DEFAULT_ABBREV;
+	revs->ignore_merges = 1;
+	revs->simplify_history = 1;
+	DIFF_OPT_SET(&revs->pruning, RECURSIVE);
+	DIFF_OPT_SET(&revs->pruning, QUICK);
+	revs->pruning.add_remove = file_add_remove;
+	revs->pruning.change = file_change;
+	revs->lifo = 1;
+	revs->dense = 1;
+	revs->prefix = prefix;
+	revs->max_age = -1;
+	revs->min_age = -1;
+	revs->skip_count = -1;
+	revs->max_count = -1;
+	revs->max_parents = -1;
+
+	revs->commit_format = CMIT_FMT_DEFAULT;
+
+	revs->grep_filter.status_only = 1;
+	revs->grep_filter.pattern_tail = &(revs->grep_filter.pattern_list);
+	revs->grep_filter.header_tail = &(revs->grep_filter.header_list);
+	revs->grep_filter.regflags = REG_NEWLINE;
+
+	diff_setup(&revs->diffopt);
+	if (prefix && !revs->diffopt.prefix) {
+		revs->diffopt.prefix = prefix;
+		revs->diffopt.prefix_length = strlen(prefix);
+	}
+}
+
+static void add_pending_commit_list(struct rev_info *revs,
+                                    struct commit_list *commit_list,
+                                    unsigned int flags)
+{
+	while (commit_list) {
+		struct object *object = &commit_list->item->object;
+		object->flags |= flags;
+		add_pending_object(revs, object, sha1_to_hex(object->sha1));
+		commit_list = commit_list->next;
+	}
+}
+
+static void prepare_show_merge(struct rev_info *revs)
+{
+	struct commit_list *bases;
+	struct commit *head, *other;
+	unsigned char sha1[20];
+	const char **prune = NULL;
+	int i, prune_num = 1; /* counting terminating NULL */
+
+	if (get_sha1("HEAD", sha1) || !(head = lookup_commit(sha1)))
+		die("--merge without HEAD?");
+	if (get_sha1("MERGE_HEAD", sha1) || !(other = lookup_commit(sha1)))
+		die("--merge without MERGE_HEAD?");
+	add_pending_object(revs, &head->object, "HEAD");
+	add_pending_object(revs, &other->object, "MERGE_HEAD");
+	bases = get_merge_bases(head, other, 1);
+	add_pending_commit_list(revs, bases, UNINTERESTING);
+	free_commit_list(bases);
+	head->object.flags |= SYMMETRIC_LEFT;
+
+	if (!active_nr)
+		read_cache();
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (!ce_stage(ce))
+			continue;
+		if (ce_path_match(ce, &revs->prune_data)) {
+			prune_num++;
+			prune = xrealloc(prune, sizeof(*prune) * prune_num);
+			prune[prune_num-2] = ce->name;
+			prune[prune_num-1] = NULL;
+		}
+		while ((i+1 < active_nr) &&
+		       ce_same_name(ce, active_cache[i+1]))
+			i++;
+	}
+	free_pathspec(&revs->prune_data);
+	init_pathspec(&revs->prune_data, prune);
+	revs->limited = 1;
+}
+
+int handle_revision_arg(const char *arg, struct rev_info *revs,
+			int flags,
+			int cant_be_filename)
+{
+	unsigned mode;
+	char *dotdot;
+	struct object *object;
+	unsigned char sha1[20];
+	int local_flags;
+
+	dotdot = strstr(arg, "..");
+	if (dotdot) {
+		unsigned char from_sha1[20];
+		const char *next = dotdot + 2;
+		const char *this = arg;
+		int symmetric = *next == '.';
+		unsigned int flags_exclude = flags ^ UNINTERESTING;
+
+		*dotdot = 0;
+		next += symmetric;
+
+		if (!*next)
+			next = "HEAD";
+		if (dotdot == arg)
+			this = "HEAD";
+		if (!get_sha1(this, from_sha1) &&
+		    !get_sha1(next, sha1)) {
+			struct commit *a, *b;
+			struct commit_list *exclude;
+
+			a = lookup_commit_reference(from_sha1);
+			b = lookup_commit_reference(sha1);
+			if (!a || !b) {
+				die(symmetric ?
+				    "Invalid symmetric difference expression %s...%s" :
+				    "Invalid revision range %s..%s",
+				    arg, next);
+			}
+
+			if (!cant_be_filename) {
+				*dotdot = '.';
+				verify_non_filename(revs->prefix, arg);
+			}
+
+			if (symmetric) {
+				exclude = get_merge_bases(a, b, 1);
+				add_pending_commit_list(revs, exclude,
+							flags_exclude);
+				free_commit_list(exclude);
+				a->object.flags |= flags | SYMMETRIC_LEFT;
+			} else
+				a->object.flags |= flags_exclude;
+			b->object.flags |= flags;
+			add_pending_object(revs, &a->object, this);
+			add_pending_object(revs, &b->object, next);
+			return 0;
+		}
+		*dotdot = '.';
+	}
+	dotdot = strstr(arg, "^@");
+	if (dotdot && !dotdot[2]) {
+		*dotdot = 0;
+		if (add_parents_only(revs, arg, flags))
+			return 0;
+		*dotdot = '^';
+	}
+	dotdot = strstr(arg, "^!");
+	if (dotdot && !dotdot[2]) {
+		*dotdot = 0;
+		if (!add_parents_only(revs, arg, flags ^ UNINTERESTING))
+			*dotdot = '^';
+	}
+
+	local_flags = 0;
+	if (*arg == '^') {
+		local_flags = UNINTERESTING;
+		arg++;
+	}
+	if (get_sha1_with_mode(arg, sha1, &mode))
+		return -1;
+	if (!cant_be_filename)
+		verify_non_filename(revs->prefix, arg);
+	object = get_reference(revs, arg, sha1, flags ^ local_flags);
+	add_pending_object_with_mode(revs, object, arg, mode);
+	return 0;
+}
+
+static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb, const char ***prune_data)
+{
+	const char **prune = *prune_data;
+	int prune_nr;
+	int prune_alloc;
+
+	/* count existing ones */
+	if (!prune)
+		prune_nr = 0;
+	else
+		for (prune_nr = 0; prune[prune_nr]; prune_nr++)
+			;
+	prune_alloc = prune_nr; /* not really, but we do not know */
+
+	while (strbuf_getwholeline(sb, stdin, '\n') != EOF) {
+		int len = sb->len;
+		if (len && sb->buf[len - 1] == '\n')
+			sb->buf[--len] = '\0';
+		ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+		prune[prune_nr++] = xstrdup(sb->buf);
+	}
+	if (prune) {
+		ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+		prune[prune_nr] = NULL;
+	}
+	*prune_data = prune;
+}
+
+static void read_revisions_from_stdin(struct rev_info *revs, const char ***prune)
+{
+	struct strbuf sb;
+	int seen_dashdash = 0;
+
+	strbuf_init(&sb, 1000);
+	while (strbuf_getwholeline(&sb, stdin, '\n') != EOF) {
+		int len = sb.len;
+		if (len && sb.buf[len - 1] == '\n')
+			sb.buf[--len] = '\0';
+		if (!len)
+			break;
+		if (sb.buf[0] == '-') {
+			if (len == 2 && sb.buf[1] == '-') {
+				seen_dashdash = 1;
+				break;
+			}
+			die("options not supported in --stdin mode");
+		}
+		if (handle_revision_arg(sb.buf, revs, 0, 1))
+			die("bad revision '%s'", sb.buf);
+	}
+	if (seen_dashdash)
+		read_pathspec_from_stdin(revs, &sb, prune);
+	strbuf_release(&sb);
+}
+
+static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
+{
+	append_grep_pattern(&revs->grep_filter, ptn, "command line", 0, what);
+}
+
+static void add_header_grep(struct rev_info *revs, enum grep_header_field field, const char *pattern)
+{
+	append_header_grep_pattern(&revs->grep_filter, field, pattern);
+}
+
+static void add_message_grep(struct rev_info *revs, const char *pattern)
+{
+	add_grep(revs, pattern, GREP_PATTERN_BODY);
+}
+
+static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
+			       int *unkc, const char **unkv)
+{
+	const char *arg = argv[0];
+	const char *optarg;
+	int argcount;
+
+	/* pseudo revision arguments */
+	if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
+	    !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
+	    !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
+	    !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
+	    !strcmp(arg, "--bisect"))
+	{
+		unkv[(*unkc)++] = arg;
+		return 1;
+	}
+
+	if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
+		revs->max_count = atoi(optarg);
+		revs->no_walk = 0;
+		return argcount;
+	} else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
+		revs->skip_count = atoi(optarg);
+		return argcount;
+	} else if ((*arg == '-') && isdigit(arg[1])) {
+	/* accept -<digit>, like traditional "head" */
+		revs->max_count = atoi(arg + 1);
+		revs->no_walk = 0;
+	} else if (!strcmp(arg, "-n")) {
+		if (argc <= 1)
+			return error("-n requires an argument");
+		revs->max_count = atoi(argv[1]);
+		revs->no_walk = 0;
+		return 2;
+	} else if (!prefixcmp(arg, "-n")) {
+		revs->max_count = atoi(arg + 2);
+		revs->no_walk = 0;
+	} else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
+		revs->max_age = atoi(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("since", argv, &optarg))) {
+		revs->max_age = approxidate(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("after", argv, &optarg))) {
+		revs->max_age = approxidate(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
+		revs->min_age = atoi(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("before", argv, &optarg))) {
+		revs->min_age = approxidate(optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("until", argv, &optarg))) {
+		revs->min_age = approxidate(optarg);
+		return argcount;
+	} else if (!strcmp(arg, "--first-parent")) {
+		revs->first_parent_only = 1;
+	} else if (!strcmp(arg, "--ancestry-path")) {
+		revs->ancestry_path = 1;
+		revs->simplify_history = 0;
+		revs->limited = 1;
+	} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
+		init_reflog_walk(&revs->reflog_info);
+	} else if (!strcmp(arg, "--default")) {
+		if (argc <= 1)
+			return error("bad --default argument");
+		revs->def = argv[1];
+		return 2;
+	} else if (!strcmp(arg, "--merge")) {
+		revs->show_merge = 1;
+	} else if (!strcmp(arg, "--topo-order")) {
+		revs->lifo = 1;
+		revs->topo_order = 1;
+	} else if (!strcmp(arg, "--simplify-merges")) {
+		revs->simplify_merges = 1;
+		revs->rewrite_parents = 1;
+		revs->simplify_history = 0;
+		revs->limited = 1;
+	} else if (!strcmp(arg, "--simplify-by-decoration")) {
+		revs->simplify_merges = 1;
+		revs->rewrite_parents = 1;
+		revs->simplify_history = 0;
+		revs->simplify_by_decoration = 1;
+		revs->limited = 1;
+		revs->prune = 1;
+		load_ref_decorations(DECORATE_SHORT_REFS);
+	} else if (!strcmp(arg, "--date-order")) {
+		revs->lifo = 0;
+		revs->topo_order = 1;
+	} else if (!prefixcmp(arg, "--early-output")) {
+		int count = 100;
+		switch (arg[14]) {
+		case '=':
+			count = atoi(arg+15);
+			/* Fallthrough */
+		case 0:
+			revs->topo_order = 1;
+		       revs->early_output = count;
+		}
+	} else if (!strcmp(arg, "--parents")) {
+		revs->rewrite_parents = 1;
+		revs->print_parents = 1;
+	} else if (!strcmp(arg, "--dense")) {
+		revs->dense = 1;
+	} else if (!strcmp(arg, "--sparse")) {
+		revs->dense = 0;
+	} else if (!strcmp(arg, "--show-all")) {
+		revs->show_all = 1;
+	} else if (!strcmp(arg, "--remove-empty")) {
+		revs->remove_empty_trees = 1;
+	} else if (!strcmp(arg, "--merges")) {
+		revs->min_parents = 2;
+	} else if (!strcmp(arg, "--no-merges")) {
+		revs->max_parents = 1;
+	} else if (!prefixcmp(arg, "--min-parents=")) {
+		revs->min_parents = atoi(arg+14);
+	} else if (!prefixcmp(arg, "--no-min-parents")) {
+		revs->min_parents = 0;
+	} else if (!prefixcmp(arg, "--max-parents=")) {
+		revs->max_parents = atoi(arg+14);
+	} else if (!prefixcmp(arg, "--no-max-parents")) {
+		revs->max_parents = -1;
+	} else if (!strcmp(arg, "--boundary")) {
+		revs->boundary = 1;
+	} else if (!strcmp(arg, "--left-right")) {
+		revs->left_right = 1;
+	} else if (!strcmp(arg, "--left-only")) {
+		if (revs->right_only)
+			die("--left-only is incompatible with --right-only"
+			    " or --cherry");
+		revs->left_only = 1;
+	} else if (!strcmp(arg, "--right-only")) {
+		if (revs->left_only)
+			die("--right-only is incompatible with --left-only");
+		revs->right_only = 1;
+	} else if (!strcmp(arg, "--cherry")) {
+		if (revs->left_only)
+			die("--cherry is incompatible with --left-only");
+		revs->cherry_mark = 1;
+		revs->right_only = 1;
+		revs->max_parents = 1;
+		revs->limited = 1;
+	} else if (!strcmp(arg, "--count")) {
+		revs->count = 1;
+	} else if (!strcmp(arg, "--cherry-mark")) {
+		if (revs->cherry_pick)
+			die("--cherry-mark is incompatible with --cherry-pick");
+		revs->cherry_mark = 1;
+		revs->limited = 1; /* needs limit_list() */
+	} else if (!strcmp(arg, "--cherry-pick")) {
+		if (revs->cherry_mark)
+			die("--cherry-pick is incompatible with --cherry-mark");
+		revs->cherry_pick = 1;
+		revs->limited = 1;
+	} else if (!strcmp(arg, "--objects")) {
+		revs->tag_objects = 1;
+		revs->tree_objects = 1;
+		revs->blob_objects = 1;
+	} else if (!strcmp(arg, "--objects-edge")) {
+		revs->tag_objects = 1;
+		revs->tree_objects = 1;
+		revs->blob_objects = 1;
+		revs->edge_hint = 1;
+	} else if (!strcmp(arg, "--unpacked")) {
+		revs->unpacked = 1;
+	} else if (!prefixcmp(arg, "--unpacked=")) {
+		die("--unpacked=<packfile> no longer supported.");
+	} else if (!strcmp(arg, "-r")) {
+		revs->diff = 1;
+		DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
+	} else if (!strcmp(arg, "-t")) {
+		revs->diff = 1;
+		DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
+		DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
+	} else if (!strcmp(arg, "-m")) {
+		revs->ignore_merges = 0;
+	} else if (!strcmp(arg, "-c")) {
+		revs->diff = 1;
+		revs->dense_combined_merges = 0;
+		revs->combine_merges = 1;
+	} else if (!strcmp(arg, "--cc")) {
+		revs->diff = 1;
+		revs->dense_combined_merges = 1;
+		revs->combine_merges = 1;
+	} else if (!strcmp(arg, "-v")) {
+		revs->verbose_header = 1;
+	} else if (!strcmp(arg, "--pretty")) {
+		revs->verbose_header = 1;
+		revs->pretty_given = 1;
+		get_commit_format(arg+8, revs);
+	} else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+		/*
+		 * Detached form ("--pretty X" as opposed to "--pretty=X")
+		 * not allowed, since the argument is optional.
+		 */
+		revs->verbose_header = 1;
+		revs->pretty_given = 1;
+		get_commit_format(arg+9, revs);
+	} else if (!strcmp(arg, "--show-notes")) {
+		revs->show_notes = 1;
+		revs->show_notes_given = 1;
+	} else if (!prefixcmp(arg, "--show-notes=")) {
+		struct strbuf buf = STRBUF_INIT;
+		revs->show_notes = 1;
+		revs->show_notes_given = 1;
+		if (!revs->notes_opt.extra_notes_refs)
+			revs->notes_opt.extra_notes_refs = xcalloc(1, sizeof(struct string_list));
+		if (!prefixcmp(arg+13, "refs/"))
+			/* happy */;
+		else if (!prefixcmp(arg+13, "notes/"))
+			strbuf_addstr(&buf, "refs/");
+		else
+			strbuf_addstr(&buf, "refs/notes/");
+		strbuf_addstr(&buf, arg+13);
+		string_list_append(revs->notes_opt.extra_notes_refs,
+				   strbuf_detach(&buf, NULL));
+	} else if (!strcmp(arg, "--no-notes")) {
+		revs->show_notes = 0;
+		revs->show_notes_given = 1;
+	} else if (!strcmp(arg, "--standard-notes")) {
+		revs->show_notes_given = 1;
+		revs->notes_opt.suppress_default_notes = 0;
+	} else if (!strcmp(arg, "--no-standard-notes")) {
+		revs->notes_opt.suppress_default_notes = 1;
+	} else if (!strcmp(arg, "--oneline")) {
+		revs->verbose_header = 1;
+		get_commit_format("oneline", revs);
+		revs->pretty_given = 1;
+		revs->abbrev_commit = 1;
+	} else if (!strcmp(arg, "--graph")) {
+		revs->topo_order = 1;
+		revs->rewrite_parents = 1;
+		revs->graph = graph_init(revs);
+	} else if (!strcmp(arg, "--root")) {
+		revs->show_root_diff = 1;
+	} else if (!strcmp(arg, "--no-commit-id")) {
+		revs->no_commit_id = 1;
+	} else if (!strcmp(arg, "--always")) {
+		revs->always_show_header = 1;
+	} else if (!strcmp(arg, "--no-abbrev")) {
+		revs->abbrev = 0;
+	} else if (!strcmp(arg, "--abbrev")) {
+		revs->abbrev = DEFAULT_ABBREV;
+	} else if (!prefixcmp(arg, "--abbrev=")) {
+		revs->abbrev = strtoul(arg + 9, NULL, 10);
+		if (revs->abbrev < MINIMUM_ABBREV)
+			revs->abbrev = MINIMUM_ABBREV;
+		else if (revs->abbrev > 40)
+			revs->abbrev = 40;
+	} else if (!strcmp(arg, "--abbrev-commit")) {
+		revs->abbrev_commit = 1;
+	} else if (!strcmp(arg, "--full-diff")) {
+		revs->diff = 1;
+		revs->full_diff = 1;
+	} else if (!strcmp(arg, "--full-history")) {
+		revs->simplify_history = 0;
+	} else if (!strcmp(arg, "--relative-date")) {
+		revs->date_mode = DATE_RELATIVE;
+		revs->date_mode_explicit = 1;
+	} else if ((argcount = parse_long_opt("date", argv, &optarg))) {
+		revs->date_mode = parse_date_format(optarg);
+		revs->date_mode_explicit = 1;
+		return argcount;
+	} else if (!strcmp(arg, "--log-size")) {
+		revs->show_log_size = 1;
+	}
+	/*
+	 * Grepping the commit log
+	 */
+	else if ((argcount = parse_long_opt("author", argv, &optarg))) {
+		add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
+		add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
+		return argcount;
+	} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
+		add_message_grep(revs, optarg);
+		return argcount;
+	} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
+		revs->grep_filter.regflags |= REG_EXTENDED;
+	} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
+		revs->grep_filter.regflags |= REG_ICASE;
+	} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
+		revs->grep_filter.fixed = 1;
+	} else if (!strcmp(arg, "--all-match")) {
+		revs->grep_filter.all_match = 1;
+	} else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+		if (strcmp(optarg, "none"))
+			git_log_output_encoding = xstrdup(optarg);
+		else
+			git_log_output_encoding = "";
+		return argcount;
+	} else if (!strcmp(arg, "--reverse")) {
+		revs->reverse ^= 1;
+	} else if (!strcmp(arg, "--children")) {
+		revs->children.name = "children";
+		revs->limited = 1;
+	} else {
+		int opts = diff_opt_parse(&revs->diffopt, argv, argc);
+		if (!opts)
+			unkv[(*unkc)++] = arg;
+		return opts;
+	}
+
+	return 1;
+}
+
+void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
+			const struct option *options,
+			const char * const usagestr[])
+{
+	int n = handle_revision_opt(revs, ctx->argc, ctx->argv,
+				    &ctx->cpidx, ctx->out);
+	if (n <= 0) {
+		error("unknown option `%s'", ctx->argv[0]);
+		usage_with_options(usagestr, options);
+	}
+	ctx->argv += n;
+	ctx->argc -= n;
+}
+
+static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
+}
+
+static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+	return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
+}
+
+static void append_prune_data(const char ***prune_data, const char **av)
+{
+	const char **prune = *prune_data;
+	int prune_nr;
+	int prune_alloc;
+
+	if (!prune) {
+		*prune_data = av;
+		return;
+	}
+
+	/* count existing ones */
+	for (prune_nr = 0; prune[prune_nr]; prune_nr++)
+		;
+	prune_alloc = prune_nr; /* not really, but we do not know */
+
+	while (*av) {
+		ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+		prune[prune_nr++] = *av;
+		av++;
+	}
+	if (prune) {
+		ALLOC_GROW(prune, prune_nr+1, prune_alloc);
+		prune[prune_nr] = NULL;
+	}
+	*prune_data = prune;
+}
+
+/*
+ * Parse revision information, filling in the "rev_info" structure,
+ * and removing the used arguments from the argument list.
+ *
+ * Returns the number of arguments left that weren't recognized
+ * (which are also moved to the head of the argument list)
+ */
+int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
+{
+	int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
+	const char **prune_data = NULL;
+	const char *submodule = NULL;
+	const char *optarg;
+	int argcount;
+
+	if (opt)
+		submodule = opt->submodule;
+
+	/* First, search for "--" */
+	seen_dashdash = 0;
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (strcmp(arg, "--"))
+			continue;
+		argv[i] = NULL;
+		argc = i;
+		if (argv[i + 1])
+			prune_data = argv + i + 1;
+		seen_dashdash = 1;
+		break;
+	}
+
+	/* Second, deal with arguments and options */
+	flags = 0;
+	read_from_stdin = 0;
+	for (left = i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (*arg == '-') {
+			int opts;
+
+			if (!strcmp(arg, "--all")) {
+				handle_refs(submodule, revs, flags, for_each_ref_submodule);
+				handle_refs(submodule, revs, flags, head_ref_submodule);
+				continue;
+			}
+			if (!strcmp(arg, "--branches")) {
+				handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
+				continue;
+			}
+			if (!strcmp(arg, "--bisect")) {
+				handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
+				handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
+				revs->bisect = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--tags")) {
+				handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
+				continue;
+			}
+			if (!strcmp(arg, "--remotes")) {
+				handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
+				continue;
+			}
+			if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
+				struct all_refs_cb cb;
+				i += argcount - 1;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref(handle_one_ref, optarg, &cb);
+				continue;
+			}
+			if (!prefixcmp(arg, "--branches=")) {
+				struct all_refs_cb cb;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
+				continue;
+			}
+			if (!prefixcmp(arg, "--tags=")) {
+				struct all_refs_cb cb;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
+				continue;
+			}
+			if (!prefixcmp(arg, "--remotes=")) {
+				struct all_refs_cb cb;
+				init_all_refs_cb(&cb, revs, flags);
+				for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
+				continue;
+			}
+			if (!strcmp(arg, "--reflog")) {
+				handle_reflog(revs, flags);
+				continue;
+			}
+			if (!strcmp(arg, "--not")) {
+				flags ^= UNINTERESTING;
+				continue;
+			}
+			if (!strcmp(arg, "--no-walk")) {
+				revs->no_walk = 1;
+				continue;
+			}
+			if (!strcmp(arg, "--do-walk")) {
+				revs->no_walk = 0;
+				continue;
+			}
+			if (!strcmp(arg, "--stdin")) {
+				if (revs->disable_stdin) {
+					argv[left++] = arg;
+					continue;
+				}
+				if (read_from_stdin++)
+					die("--stdin given twice?");
+				read_revisions_from_stdin(revs, &prune_data);
+				continue;
+			}
+
+			opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv);
+			if (opts > 0) {
+				i += opts - 1;
+				continue;
+			}
+			if (opts < 0)
+				exit(128);
+			continue;
+		}
+
+		if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
+			int j;
+			if (seen_dashdash || *arg == '^')
+				die("bad revision '%s'", arg);
+
+			/* If we didn't have a "--":
+			 * (1) all filenames must exist;
+			 * (2) all rev-args must not be interpretable
+			 *     as a valid filename.
+			 * but the latter we have checked in the main loop.
+			 */
+			for (j = i; j < argc; j++)
+				verify_filename(revs->prefix, argv[j]);
+
+			append_prune_data(&prune_data, argv + i);
+			break;
+		}
+		else
+			got_rev_arg = 1;
+	}
+
+	if (prune_data)
+		init_pathspec(&revs->prune_data, get_pathspec(revs->prefix, prune_data));
+
+	if (revs->def == NULL)
+		revs->def = opt ? opt->def : NULL;
+	if (opt && opt->tweak)
+		opt->tweak(revs, opt);
+	if (revs->show_merge)
+		prepare_show_merge(revs);
+	if (revs->def && !revs->pending.nr && !got_rev_arg) {
+		unsigned char sha1[20];
+		struct object *object;
+		unsigned mode;
+		if (get_sha1_with_mode(revs->def, sha1, &mode))
+			die("bad default revision '%s'", revs->def);
+		object = get_reference(revs, revs->def, sha1, 0);
+		add_pending_object_with_mode(revs, object, revs->def, mode);
+	}
+
+	/* Did the user ask for any diff output? Run the diff! */
+	if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT)
+		revs->diff = 1;
+
+	/* Pickaxe, diff-filter and rename following need diffs */
+	if (revs->diffopt.pickaxe ||
+	    revs->diffopt.filter ||
+	    DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+		revs->diff = 1;
+
+	if (revs->topo_order)
+		revs->limited = 1;
+
+	if (revs->prune_data.nr) {
+		diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
+		/* Can't prune commits with rename following: the paths change.. */
+		if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+			revs->prune = 1;
+		if (!revs->full_diff)
+			diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
+	}
+	if (revs->combine_merges)
+		revs->ignore_merges = 0;
+	revs->diffopt.abbrev = revs->abbrev;
+	if (diff_setup_done(&revs->diffopt) < 0)
+		die("diff_setup_done failed");
+
+	compile_grep_patterns(&revs->grep_filter);
+
+	if (revs->reverse && revs->reflog_info)
+		die("cannot combine --reverse with --walk-reflogs");
+	if (revs->rewrite_parents && revs->children.name)
+		die("cannot combine --parents and --children");
+
+	/*
+	 * Limitations on the graph functionality
+	 */
+	if (revs->reverse && revs->graph)
+		die("cannot combine --reverse with --graph");
+
+	if (revs->reflog_info && revs->graph)
+		die("cannot combine --walk-reflogs with --graph");
+
+	return left;
+}
+
+static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
+{
+	struct commit_list *l = xcalloc(1, sizeof(*l));
+
+	l->item = child;
+	l->next = add_decoration(&revs->children, &parent->object, l);
+}
+
+static int remove_duplicate_parents(struct commit *commit)
+{
+	struct commit_list **pp, *p;
+	int surviving_parents;
+
+	/* Examine existing parents while marking ones we have seen... */
+	pp = &commit->parents;
+	while ((p = *pp) != NULL) {
+		struct commit *parent = p->item;
+		if (parent->object.flags & TMP_MARK) {
+			*pp = p->next;
+			continue;
+		}
+		parent->object.flags |= TMP_MARK;
+		pp = &p->next;
+	}
+	/* count them while clearing the temporary mark */
+	surviving_parents = 0;
+	for (p = commit->parents; p; p = p->next) {
+		p->item->object.flags &= ~TMP_MARK;
+		surviving_parents++;
+	}
+	return surviving_parents;
+}
+
+struct merge_simplify_state {
+	struct commit *simplified;
+};
+
+static struct merge_simplify_state *locate_simplify_state(struct rev_info *revs, struct commit *commit)
+{
+	struct merge_simplify_state *st;
+
+	st = lookup_decoration(&revs->merge_simplification, &commit->object);
+	if (!st) {
+		st = xcalloc(1, sizeof(*st));
+		add_decoration(&revs->merge_simplification, &commit->object, st);
+	}
+	return st;
+}
+
+static struct commit_list **simplify_one(struct rev_info *revs, struct commit *commit, struct commit_list **tail)
+{
+	struct commit_list *p;
+	struct merge_simplify_state *st, *pst;
+	int cnt;
+
+	st = locate_simplify_state(revs, commit);
+
+	/*
+	 * Have we handled this one?
+	 */
+	if (st->simplified)
+		return tail;
+
+	/*
+	 * An UNINTERESTING commit simplifies to itself, so does a
+	 * root commit.  We do not rewrite parents of such commit
+	 * anyway.
+	 */
+	if ((commit->object.flags & UNINTERESTING) || !commit->parents) {
+		st->simplified = commit;
+		return tail;
+	}
+
+	/*
+	 * Do we know what commit all of our parents should be rewritten to?
+	 * Otherwise we are not ready to rewrite this one yet.
+	 */
+	for (cnt = 0, p = commit->parents; p; p = p->next) {
+		pst = locate_simplify_state(revs, p->item);
+		if (!pst->simplified) {
+			tail = &commit_list_insert(p->item, tail)->next;
+			cnt++;
+		}
+	}
+	if (cnt) {
+		tail = &commit_list_insert(commit, tail)->next;
+		return tail;
+	}
+
+	/*
+	 * Rewrite our list of parents.
+	 */
+	for (p = commit->parents; p; p = p->next) {
+		pst = locate_simplify_state(revs, p->item);
+		p->item = pst->simplified;
+	}
+	cnt = remove_duplicate_parents(commit);
+
+	/*
+	 * It is possible that we are a merge and one side branch
+	 * does not have any commit that touches the given paths;
+	 * in such a case, the immediate parents will be rewritten
+	 * to different commits.
+	 *
+	 *      o----X		X: the commit we are looking at;
+	 *     /    /		o: a commit that touches the paths;
+	 * ---o----'
+	 *
+	 * Further reduce the parents by removing redundant parents.
+	 */
+	if (1 < cnt) {
+		struct commit_list *h = reduce_heads(commit->parents);
+		cnt = commit_list_count(h);
+		free_commit_list(commit->parents);
+		commit->parents = h;
+	}
+
+	/*
+	 * A commit simplifies to itself if it is a root, if it is
+	 * UNINTERESTING, if it touches the given paths, or if it is a
+	 * merge and its parents simplifies to more than one commits
+	 * (the first two cases are already handled at the beginning of
+	 * this function).
+	 *
+	 * Otherwise, it simplifies to what its sole parent simplifies to.
+	 */
+	if (!cnt ||
+	    (commit->object.flags & UNINTERESTING) ||
+	    !(commit->object.flags & TREESAME) ||
+	    (1 < cnt))
+		st->simplified = commit;
+	else {
+		pst = locate_simplify_state(revs, commit->parents->item);
+		st->simplified = pst->simplified;
+	}
+	return tail;
+}
+
+static void simplify_merges(struct rev_info *revs)
+{
+	struct commit_list *list;
+	struct commit_list *yet_to_do, **tail;
+
+	if (!revs->topo_order)
+		sort_in_topological_order(&revs->commits, revs->lifo);
+	if (!revs->prune)
+		return;
+
+	/* feed the list reversed */
+	yet_to_do = NULL;
+	for (list = revs->commits; list; list = list->next)
+		commit_list_insert(list->item, &yet_to_do);
+	while (yet_to_do) {
+		list = yet_to_do;
+		yet_to_do = NULL;
+		tail = &yet_to_do;
+		while (list) {
+			struct commit *commit = list->item;
+			struct commit_list *next = list->next;
+			free(list);
+			list = next;
+			tail = simplify_one(revs, commit, tail);
+		}
+	}
+
+	/* clean up the result, removing the simplified ones */
+	list = revs->commits;
+	revs->commits = NULL;
+	tail = &revs->commits;
+	while (list) {
+		struct commit *commit = list->item;
+		struct commit_list *next = list->next;
+		struct merge_simplify_state *st;
+		free(list);
+		list = next;
+		st = locate_simplify_state(revs, commit);
+		if (st->simplified == commit)
+			tail = &commit_list_insert(commit, tail)->next;
+	}
+}
+
+static void set_children(struct rev_info *revs)
+{
+	struct commit_list *l;
+	for (l = revs->commits; l; l = l->next) {
+		struct commit *commit = l->item;
+		struct commit_list *p;
+
+		for (p = commit->parents; p; p = p->next)
+			add_child(revs, p->item, commit);
+	}
+}
+
+int prepare_revision_walk(struct rev_info *revs)
+{
+	int nr = revs->pending.nr;
+	struct object_array_entry *e, *list;
+
+	e = list = revs->pending.objects;
+	revs->pending.nr = 0;
+	revs->pending.alloc = 0;
+	revs->pending.objects = NULL;
+	while (--nr >= 0) {
+		struct commit *commit = handle_commit(revs, e->item, e->name);
+		if (commit) {
+			if (!(commit->object.flags & SEEN)) {
+				commit->object.flags |= SEEN;
+				commit_list_insert_by_date(commit, &revs->commits);
+			}
+		}
+		e++;
+	}
+	free(list);
+
+	if (revs->no_walk)
+		return 0;
+	if (revs->limited)
+		if (limit_list(revs) < 0)
+			return -1;
+	if (revs->topo_order)
+		sort_in_topological_order(&revs->commits, revs->lifo);
+	if (revs->simplify_merges)
+		simplify_merges(revs);
+	if (revs->children.name)
+		set_children(revs);
+	return 0;
+}
+
+enum rewrite_result {
+	rewrite_one_ok,
+	rewrite_one_noparents,
+	rewrite_one_error
+};
+
+static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
+{
+	struct commit_list *cache = NULL;
+
+	for (;;) {
+		struct commit *p = *pp;
+		if (!revs->limited)
+			if (add_parents_to_list(revs, p, &revs->commits, &cache) < 0)
+				return rewrite_one_error;
+		if (p->parents && p->parents->next)
+			return rewrite_one_ok;
+		if (p->object.flags & UNINTERESTING)
+			return rewrite_one_ok;
+		if (!(p->object.flags & TREESAME))
+			return rewrite_one_ok;
+		if (!p->parents)
+			return rewrite_one_noparents;
+		*pp = p->parents->item;
+	}
+}
+
+static int rewrite_parents(struct rev_info *revs, struct commit *commit)
+{
+	struct commit_list **pp = &commit->parents;
+	while (*pp) {
+		struct commit_list *parent = *pp;
+		switch (rewrite_one(revs, &parent->item)) {
+		case rewrite_one_ok:
+			break;
+		case rewrite_one_noparents:
+			*pp = parent->next;
+			continue;
+		case rewrite_one_error:
+			return -1;
+		}
+		pp = &parent->next;
+	}
+	remove_duplicate_parents(commit);
+	return 0;
+}
+
+static int commit_match(struct commit *commit, struct rev_info *opt)
+{
+	if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
+		return 1;
+	return grep_buffer(&opt->grep_filter,
+			   NULL, /* we say nothing, not even filename */
+			   commit->buffer, strlen(commit->buffer));
+}
+
+static inline int want_ancestry(struct rev_info *revs)
+{
+	return (revs->rewrite_parents || revs->children.name);
+}
+
+enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
+{
+	if (commit->object.flags & SHOWN)
+		return commit_ignore;
+	if (revs->unpacked && has_sha1_pack(commit->object.sha1))
+		return commit_ignore;
+	if (revs->show_all)
+		return commit_show;
+	if (commit->object.flags & UNINTERESTING)
+		return commit_ignore;
+	if (revs->min_age != -1 && (commit->date > revs->min_age))
+		return commit_ignore;
+	if (revs->min_parents || (revs->max_parents >= 0)) {
+		int n = 0;
+		struct commit_list *p;
+		for (p = commit->parents; p; p = p->next)
+			n++;
+		if ((n < revs->min_parents) ||
+		    ((revs->max_parents >= 0) && (n > revs->max_parents)))
+			return commit_ignore;
+	}
+	if (!commit_match(commit, revs))
+		return commit_ignore;
+	if (revs->prune && revs->dense) {
+		/* Commit without changes? */
+		if (commit->object.flags & TREESAME) {
+			/* drop merges unless we want parenthood */
+			if (!want_ancestry(revs))
+				return commit_ignore;
+			/* non-merge - always ignore it */
+			if (!commit->parents || !commit->parents->next)
+				return commit_ignore;
+		}
+	}
+	return commit_show;
+}
+
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+	enum commit_action action = get_commit_action(revs, commit);
+
+	if (action == commit_show &&
+	    !revs->show_all &&
+	    revs->prune && revs->dense && want_ancestry(revs)) {
+		if (rewrite_parents(revs, commit) < 0)
+			return commit_error;
+	}
+	return action;
+}
+
+static struct commit *get_revision_1(struct rev_info *revs)
+{
+	if (!revs->commits)
+		return NULL;
+
+	do {
+		struct commit_list *entry = revs->commits;
+		struct commit *commit = entry->item;
+
+		revs->commits = entry->next;
+		free(entry);
+
+		if (revs->reflog_info) {
+			fake_reflog_parent(revs->reflog_info, commit);
+			commit->object.flags &= ~(ADDED | SEEN | SHOWN);
+		}
+
+		/*
+		 * If we haven't done the list limiting, we need to look at
+		 * the parents here. We also need to do the date-based limiting
+		 * that we'd otherwise have done in limit_list().
+		 */
+		if (!revs->limited) {
+			if (revs->max_age != -1 &&
+			    (commit->date < revs->max_age))
+				continue;
+			if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0)
+				die("Failed to traverse parents of commit %s",
+				    sha1_to_hex(commit->object.sha1));
+		}
+
+		switch (simplify_commit(revs, commit)) {
+		case commit_ignore:
+			continue;
+		case commit_error:
+			die("Failed to simplify parents of commit %s",
+			    sha1_to_hex(commit->object.sha1));
+		default:
+			return commit;
+		}
+	} while (revs->commits);
+	return NULL;
+}
+
+static void gc_boundary(struct object_array *array)
+{
+	unsigned nr = array->nr;
+	unsigned alloc = array->alloc;
+	struct object_array_entry *objects = array->objects;
+
+	if (alloc <= nr) {
+		unsigned i, j;
+		for (i = j = 0; i < nr; i++) {
+			if (objects[i].item->flags & SHOWN)
+				continue;
+			if (i != j)
+				objects[j] = objects[i];
+			j++;
+		}
+		for (i = j; i < nr; i++)
+			objects[i].item = NULL;
+		array->nr = j;
+	}
+}
+
+static void create_boundary_commit_list(struct rev_info *revs)
+{
+	unsigned i;
+	struct commit *c;
+	struct object_array *array = &revs->boundary_commits;
+	struct object_array_entry *objects = array->objects;
+
+	/*
+	 * If revs->commits is non-NULL at this point, an error occurred in
+	 * get_revision_1().  Ignore the error and continue printing the
+	 * boundary commits anyway.  (This is what the code has always
+	 * done.)
+	 */
+	if (revs->commits) {
+		free_commit_list(revs->commits);
+		revs->commits = NULL;
+	}
+
+	/*
+	 * Put all of the actual boundary commits from revs->boundary_commits
+	 * into revs->commits
+	 */
+	for (i = 0; i < array->nr; i++) {
+		c = (struct commit *)(objects[i].item);
+		if (!c)
+			continue;
+		if (!(c->object.flags & CHILD_SHOWN))
+			continue;
+		if (c->object.flags & (SHOWN | BOUNDARY))
+			continue;
+		c->object.flags |= BOUNDARY;
+		commit_list_insert(c, &revs->commits);
+	}
+
+	/*
+	 * If revs->topo_order is set, sort the boundary commits
+	 * in topological order
+	 */
+	sort_in_topological_order(&revs->commits, revs->lifo);
+}
+
+static struct commit *get_revision_internal(struct rev_info *revs)
+{
+	struct commit *c = NULL;
+	struct commit_list *l;
+
+	if (revs->boundary == 2) {
+		/*
+		 * All of the normal commits have already been returned,
+		 * and we are now returning boundary commits.
+		 * create_boundary_commit_list() has populated
+		 * revs->commits with the remaining commits to return.
+		 */
+		c = pop_commit(&revs->commits);
+		if (c)
+			c->object.flags |= SHOWN;
+		return c;
+	}
+
+	/*
+	 * Now pick up what they want to give us
+	 */
+	c = get_revision_1(revs);
+	if (c) {
+		while (0 < revs->skip_count) {
+			revs->skip_count--;
+			c = get_revision_1(revs);
+			if (!c)
+				break;
+		}
+	}
+
+	/*
+	 * Check the max_count.
+	 */
+	switch (revs->max_count) {
+	case -1:
+		break;
+	case 0:
+		c = NULL;
+		break;
+	default:
+		revs->max_count--;
+	}
+
+	if (c)
+		c->object.flags |= SHOWN;
+
+	if (!revs->boundary) {
+		return c;
+	}
+
+	if (!c) {
+		/*
+		 * get_revision_1() runs out the commits, and
+		 * we are done computing the boundaries.
+		 * switch to boundary commits output mode.
+		 */
+		revs->boundary = 2;
+
+		/*
+		 * Update revs->commits to contain the list of
+		 * boundary commits.
+		 */
+		create_boundary_commit_list(revs);
+
+		return get_revision_internal(revs);
+	}
+
+	/*
+	 * boundary commits are the commits that are parents of the
+	 * ones we got from get_revision_1() but they themselves are
+	 * not returned from get_revision_1().  Before returning
+	 * 'c', we need to mark its parents that they could be boundaries.
+	 */
+
+	for (l = c->parents; l; l = l->next) {
+		struct object *p;
+		p = &(l->item->object);
+		if (p->flags & (CHILD_SHOWN | SHOWN))
+			continue;
+		p->flags |= CHILD_SHOWN;
+		gc_boundary(&revs->boundary_commits);
+		add_object_array(p, NULL, &revs->boundary_commits);
+	}
+
+	return c;
+}
+
+struct commit *get_revision(struct rev_info *revs)
+{
+	struct commit *c;
+	struct commit_list *reversed;
+
+	if (revs->reverse) {
+		reversed = NULL;
+		while ((c = get_revision_internal(revs))) {
+			commit_list_insert(c, &reversed);
+		}
+		revs->commits = reversed;
+		revs->reverse = 0;
+		revs->reverse_output_stage = 1;
+	}
+
+	if (revs->reverse_output_stage)
+		return pop_commit(&revs->commits);
+
+	c = get_revision_internal(revs);
+	if (c && revs->graph)
+		graph_update(revs->graph, c);
+	return c;
+}
+
+char *get_revision_mark(const struct rev_info *revs, const struct commit *commit)
+{
+	if (commit->object.flags & BOUNDARY)
+		return "-";
+	else if (commit->object.flags & UNINTERESTING)
+		return "^";
+	else if (commit->object.flags & PATCHSAME)
+		return "=";
+	else if (!revs || revs->left_right) {
+		if (commit->object.flags & SYMMETRIC_LEFT)
+			return "<";
+		else
+			return ">";
+	} else if (revs->graph)
+		return "*";
+	else if (revs->cherry_mark)
+		return "+";
+	return "";
+}
+
+void put_revision_mark(const struct rev_info *revs, const struct commit *commit)
+{
+	char *mark = get_revision_mark(revs, commit);
+	if (!strlen(mark))
+		return;
+	fputs(mark, stdout);
+	putchar(' ');
+}
diff --git a/t/t5815-submodule-protos.sh b/t/t5815-submodule-protos.sh
new file mode 100755
index 00000000000000..06f55a1b8a0b57
--- /dev/null
+++ b/t/t5815-submodule-protos.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='test protocol whitelisting with submodules'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-proto-disable.sh
+
+setup_ext_wrapper
+setup_ssh_wrapper
+
+test_expect_success 'setup repository with submodules' '
+	mkdir remote &&
+	git init remote/repo.git &&
+	(cd remote/repo.git && test_commit one) &&
+	# submodule-add should probably trust what we feed it on the cmdline,
+	# but its implementation is overly conservative.
+	GIT_ALLOW_PROTOCOL=ssh git submodule add remote:repo.git ssh-module &&
+	GIT_ALLOW_PROTOCOL=ext git submodule add "ext::fake-remote %S repo.git" ext-module &&
+	git commit -m "add submodules"
+'
+
+test_expect_success 'clone with recurse-submodules fails' '
+	test_must_fail git clone --recurse-submodules . dst
+'
+
+test_expect_success 'setup individual updates' '
+	rm -rf dst &&
+	git clone . dst &&
+	git -C dst submodule init
+'
+
+test_expect_success 'update of ssh allowed' '
+	git -C dst submodule update ssh-module
+'
+
+test_expect_success 'update of ext not allowed' '
+	test_must_fail git -C dst submodule update ext-module
+'
+
+test_expect_success 'user can override whitelist' '
+	GIT_ALLOW_PROTOCOL=ext git -C dst submodule update ext-module
+'
+
+test_done
diff --git a/t/t9850-shell.sh b/t/t9850-shell.sh
new file mode 100755
index 00000000000000..2af476c3afcf0a
--- /dev/null
+++ b/t/t9850-shell.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='git shell tests'
+. ./test-lib.sh
+
+test_expect_success 'shell allows upload-pack' '
+	printf 0000 >input &&
+	git upload-pack . <input >expect &&
+	git shell -c "git-upload-pack $SQ.$SQ" <input >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'shell forbids other commands' '
+	test_must_fail git shell -c "git config foo.bar baz"
+'
+
+test_expect_success 'shell forbids interactive use by default' '
+	test_must_fail git shell
+'
+
+test_expect_success 'shell allows interactive command' '
+	mkdir git-shell-commands &&
+	write_script git-shell-commands/ping <<-\EOF &&
+	echo pong
+	EOF
+	echo pong >expect &&
+	echo ping | git shell >actual &&
+	test_cmp expect actual
+'
+
+test_done