From 31ec6abf887ec95642cbe82fe61076e975494ab0 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 14 Dec 2005 21:25:22 -0800
Subject: [PATCH 01/43] clone-pack: make it usable for partial branch cloning.

clone-pack had some logic to accept subset of remote refs from
the command line and clone from there.  However, it was never
used in practice and its problems were not found out so far.

This commit changes the command to output the object names of
refs to the standard output instead of making a clone of the
remote repository when explicit <head> parameters are given; the
output format is the same as fetch-pack.

The traditional behaviour of cloning the whole repository by
giving no explicit <head> parameters stays the same.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-clone-pack.txt |  6 +++++-
 clone-pack.c                     | 13 +++++++++++--
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-clone-pack.txt b/Documentation/git-clone-pack.txt
index cfc7b62f31b..39906fc4501 100644
--- a/Documentation/git-clone-pack.txt
+++ b/Documentation/git-clone-pack.txt
@@ -43,7 +43,11 @@ OPTIONS
 	The heads to update.  This is relative to $GIT_DIR
 	(e.g. "HEAD", "refs/heads/master").  When unspecified,
 	all heads are updated to match the remote repository.
-
++
+Usually all the refs from existing repository are stored
+under the same name in the new repository.  Giving explicit
+<head> arguments instead writes the object names and refs to
+the standard output, just like get-fetch-pack does.
 
 Author
 ------
diff --git a/clone-pack.c b/clone-pack.c
index a99a95c5f26..b5ce5d31115 100644
--- a/clone-pack.c
+++ b/clone-pack.c
@@ -259,8 +259,17 @@ static int clone_pack(int fd[2], int nr_match, char **match)
 
 	status = clone_without_unpack(fd);
 
-	if (!status)
-		write_refs(refs);
+	if (!status) {
+		if (nr_match == 0)
+			write_refs(refs);
+		else
+			while (refs) {
+				printf("%s %s\n",
+				       sha1_to_hex(refs->old_sha1),
+				       refs->name);
+				refs = refs->next;
+			}
+	}
 	return status;
 }
 

From 988eece42aa4c1c6b7c4cdfd102748c19acba7ed Mon Sep 17 00:00:00 2001
From: Martin Langhoff <martin@catalyst.net.nz>
Date: Thu, 15 Dec 2005 19:26:46 +1300
Subject: [PATCH 02/43] svnimport: exit cleanly when we are up to date

Now we detect that the SVN repo does not have new commits for us and exit
cleanly, removing the lockfile. With this, svnimport supports being run
on a cronjob to maintain a SVN2GIT gateway.

Signed-off-by: Martin Langhoff <martin@catalyst.net.nz>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svnimport.perl | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/git-svnimport.perl b/git-svnimport.perl
index 65868a91e5f..cb241d1b517 100755
--- a/git-svnimport.perl
+++ b/git-svnimport.perl
@@ -736,6 +736,13 @@ sub commit_all {
 }
 
 $opt_l = $svn->{'maxrev'} if not defined $opt_l or $opt_l > $svn->{'maxrev'};
+
+if ($svn->{'maxrev'} < $current_rev) {
+    print "Up to date: no new revisions to fetch!\n" if $opt_v;
+    unlink("$git_dir/SVN2GIT_HEAD");
+    exit;
+}
+
 print "Fetching from $current_rev to $opt_l ...\n" if $opt_v;
 
 my $pool=SVN::Pool->new;

From 8431c4eb0976b0558eaa1df475ce6de6b52ce484 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 14 Dec 2005 23:08:08 -0800
Subject: [PATCH 03/43] Documentation: tutorial

At the beginning of tutorial, refer the reader to everyday if
she has not done so yet.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/tutorial.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index a61b824443b..543afb84e50 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -27,6 +27,12 @@ SCM, you can skip them during your first pass.
 [NOTE]
 And those "too deep" descriptions are often marked as Note.
 
+[NOTE]
+If you are already familiar with another version control system,
+like CVS, you may want to take a look at
+link:everyday.html[Everyday GIT in 20 commands or so] first
+before reading this.
+
 
 Creating a git repository
 -------------------------

From 6677c4665af2d73f670bec382bc82d0f2e9513fb Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 15 Dec 2005 12:54:00 -0800
Subject: [PATCH 04/43] get_sha1_basic(): corner case ambiguity fix

When .git/refs/heads/frotz and .git/refs/tags/frotz existed, and
the object name stored in .git/refs/heads/frotz were corrupt, we
ended up picking tags/frotz without complaining.  Worse yet, if
the corrupt .git/refs/heads/frotz was more than 40 bytes and
began with hexadecimal characters, it silently overwritten the
initial part of the returned result.

This commit adds a couple of tests to demonstrate these cases,
with a fix.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 sha1_name.c      | 35 ++++++++++++++++++++++++++---------
 t/t0000-basic.sh | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 t/test-lib.sh    |  1 +
 3 files changed, 75 insertions(+), 9 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index faac158b16c..bf8f0f0e1fe 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -203,11 +203,12 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
 	return NULL;
 }
 
-static int ambiguous_path(const char *path)
+static int ambiguous_path(const char *path, int len)
 {
 	int slash = 1;
+	int cnt;
 
-	for (;;) {
+	for (cnt = 0; cnt < len; cnt++) {
 		switch (*path++) {
 		case '\0':
 			break;
@@ -224,6 +225,7 @@ static int ambiguous_path(const char *path)
 		}
 		return slash;
 	}
+	return slash;
 }
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
@@ -242,26 +244,41 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 		return 0;
 
 	/* Accept only unambiguous ref paths. */
-	if (ambiguous_path(str))
+	if (ambiguous_path(str, len))
 		return -1;
 
 	for (p = prefix; *p; p++) {
 		char *pathname = git_path("%s/%.*s", *p, len, str);
+
 		if (!read_ref(pathname, sha1)) {
 			/* Must be unique; i.e. when heads/foo and
 			 * tags/foo are both present, reject "foo".
-			 * Note that read_ref() eventually calls
-			 * get_sha1_hex() which can smudge initial
-			 * part of the buffer even if what is read
-			 * is found to be invalid halfway.
 			 */
 			if (1 < found++)
 				return -1;
 		}
+
+		/* We want to allow .git/description file and
+		 * "description" branch to exist at the same time.
+		 * "git-rev-parse description" should silently skip
+		 * .git/description file as a candidate for
+		 * get_sha1().  However, having garbage file anywhere
+		 * under refs/ is not OK, and we would not have caught
+		 * ambiguous heads and tags with the above test.
+		 */
+		else if (**p && !access(pathname, F_OK)) {
+			/* Garbage exists under .git/refs */
+			return error("garbage ref found '%s'", pathname);
+		}
 	}
-	if (found == 1)
+	switch (found) {
+	case 0:
+		return -1;
+	case 1:
 		return 0;
-	return -1;
+	default:
+		return error("ambiguous refname '%.*s'", len, str);
+	}
 }
 
 static int get_sha1_1(const char *name, int len, unsigned char *sha1);
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index bc3e711a525..ffa723ea8ba 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -205,4 +205,52 @@ test_expect_success \
     'no diff after checkout and git-update-index --refresh.' \
     'git-diff-files >current && cmp -s current /dev/null'
 
+
+# extended sha1 parsing and ambiguity resolution
+
+GIT_AUTHOR_DATE='1995-01-29T16:00:00 -0800'
+GIT_AUTHOR_EMAIL=a.u.thor@example.com
+GIT_AUTHOR_NAME='A U Thor'
+GIT_COMMITTER_DATE='1995-01-29T16:00:00 -0800'
+GIT_COMMITTER_EMAIL=c.o.mmitter@example.com
+GIT_COMMITTER_NAME='C O Mmitter'
+export GIT_AUTHOR_DATE
+export GIT_AUTHOR_EMAIL
+export GIT_AUTHOR_NAME
+export GIT_COMMITTER_DATE
+export GIT_COMMITTER_EMAIL
+export GIT_COMMITTER_NAME
+
+test_expect_success \
+	'initial commit.' \
+	'commit=$(echo Initial commit | git-commit-tree $tree) &&
+	 echo "$commit" >.git/refs/heads/master &&
+	 git-ls-tree HEAD &&
+	 test "$commit" = 51a092e9ef6cbbe66d258acd17599d3f80be6162'
+
+test_expect_success \
+	'Ambiguous' \
+	'echo "$commit" >.git/refs/heads/nasty &&
+	 echo "$commit" >.git/refs/tags/nasty &&
+	 if git-rev-parse --verify nasty
+	 then
+		echo "should have barfed"
+		false
+	 else
+	 	:
+	 fi &&
+	 # names directly underneath .git/ should not interfere
+	 echo "$commit" >.git/refs/heads/description &&
+	 git-rev-parse --verify description &&
+	 # broken object name
+	 echo fffffffffffffffffffffffffffffffffffffffg \
+	 	>.git/refs/heads/nasty &&
+	 if git-rev-parse --verify nasty
+	 then
+		echo "should have barfed"
+		false
+	 else
+	 	:
+	 fi'
+
 test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 2819bef1c4a..a97d259e26b 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -18,6 +18,7 @@ unset GIT_ALTERNATE_OBJECT_DIRECTORIES
 unset GIT_AUTHOR_DATE
 unset GIT_AUTHOR_EMAIL
 unset GIT_AUTHOR_NAME
+unset GIT_COMMITTER_DATE
 unset GIT_COMMITTER_EMAIL
 unset GIT_COMMITTER_NAME
 unset GIT_DIFF_OPTS

From c6f60f991f29ce1140b9dede1185562fbffe2549 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 15 Dec 2005 13:02:25 -0800
Subject: [PATCH 05/43] applymbox: typofix

Sorry, I broke this command completely with the stupid typo.

Noticed by Marco Costalba.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-applypatch.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-applypatch.sh b/git-applypatch.sh
index e8ba34a0ad4..12cab1e0d4c 100755
--- a/git-applypatch.sh
+++ b/git-applypatch.sh
@@ -14,7 +14,7 @@
 USAGE='<msg> <patch> <info> [<signoff>]'
 . git-sh-setup
 
-case "$#" in 3|4) usage ;; esac
+case "$#" in 3|4) ;; *) usage ;; esac
 
 final=.dotest/final-commit
 ##

From 92811b57674ce81e0d7fa1cfeca44023bc650641 Mon Sep 17 00:00:00 2001
From: Fredrik Kuivinen <freku045@student.liu.se>
Date: Thu, 15 Dec 2005 23:47:57 +0100
Subject: [PATCH 06/43] git-diff: Usage string clean-up

Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-diff.sh | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/git-diff.sh b/git-diff.sh
index b62e58341cd..4812ae4c1ff 100755
--- a/git-diff.sh
+++ b/git-diff.sh
@@ -3,15 +3,14 @@
 # Copyright (c) 2005 Linus Torvalds
 # Copyright (c) 2005 Junio C Hamano
 
+USAGE='[ --diff-options ] <ent>{0,2} [<path>...]'
+SUBDIRECTORY_OK='Yes'
+. git-sh-setup
+
 rev=$(git-rev-parse --revs-only --no-flags --sq "$@") || exit
 flags=$(git-rev-parse --no-revs --flags --sq "$@")
 files=$(git-rev-parse --no-revs --no-flags --sq "$@")
 
-die () {
-    echo >&2 "$*"
-    exit 1
-}
-
 # I often say 'git diff --cached -p' and get scolded by git-diff-files, but
 # obviously I mean 'git diff --cached -p HEAD' in that case.
 case "$rev" in
@@ -40,8 +39,7 @@ esac
 
 case "$rev" in
 ?*' '?*' '?*)
-	echo >&2 "I don't understand"
-	exit 1
+	usage
 	;;
 ?*' '^?*)
 	begin=$(expr "$rev" : '.*^.\([0-9a-f]*\).*') &&
@@ -58,7 +56,7 @@ case "$rev" in
 	cmd="git-diff-files $flags -- $files"
 	;;
 *)
-	die "I don't understand $*"
+	usage
 	;;
 esac
 

From 2a58a9a92ea275c11a83269425677c95c532cc18 Mon Sep 17 00:00:00 2001
From: Fredrik Kuivinen <freku045@student.liu.se>
Date: Thu, 15 Dec 2005 23:48:26 +0100
Subject: [PATCH 07/43] git-log: Add usage string

Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-log.sh | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/git-log.sh b/git-log.sh
index b36c4e95343..c2ea71cf14a 100755
--- a/git-log.sh
+++ b/git-log.sh
@@ -3,13 +3,13 @@
 # Copyright (c) 2005 Linus Torvalds
 #
 
-# This one uses only subdirectory-aware commands, so no need to
-# include sh-setup-script.
+USAGE='[--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [git-rev-list options]'
+SUBDIRECTORY_OK='Yes'
+. git-sh-setup
 
 revs=$(git-rev-parse --revs-only --no-flags --default HEAD "$@") || exit
 [ "$revs" ] || {
-	echo >&2 "No HEAD ref"
-	exit 1
+	die "No HEAD ref"
 }
 git-rev-list --pretty $(git-rev-parse --default HEAD "$@") |
 LESS=-S ${PAGER:-less}

From 1403959cc804131a91f15117c7afd6822d0fad90 Mon Sep 17 00:00:00 2001
From: Fredrik Kuivinen <freku045@student.liu.se>
Date: Thu, 15 Dec 2005 23:48:38 +0100
Subject: [PATCH 08/43] git-whatchanged: Add usage string

Signed-off-by: Fredrik Kuivinen <freku045@student.liu.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-whatchanged.sh | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/git-whatchanged.sh b/git-whatchanged.sh
index 85a49fcd8e7..b170f74a94e 100755
--- a/git-whatchanged.sh
+++ b/git-whatchanged.sh
@@ -1,4 +1,9 @@
 #!/bin/sh
+
+USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-diff-tree options] [git-rev-list options]'
+SUBDIRECTORY_OK='Yes'
+. git-sh-setup
+
 rev_list_args=$(git-rev-parse --sq --default HEAD --revs-only "$@") &&
 diff_tree_args=$(git-rev-parse --sq --no-revs "$@") &&
 

From e5e3a9d8f91685cbbb4099f1fdfb9798461ef5d4 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Thu, 15 Dec 2005 11:10:32 -0500
Subject: [PATCH 09/43] small cleanup for diff-delta.c

This patch removes unused remnants of the original xdiff source.
No functional change.  Possible tiny speed improvement.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 diff-delta.c | 34 ++++++++++++----------------------
 1 file changed, 12 insertions(+), 22 deletions(-)

diff --git a/diff-delta.c b/diff-delta.c
index b2ae7b5e6c3..890986eeb02 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -84,20 +84,15 @@ typedef struct s_chanode {
 } chanode_t;
 
 typedef struct s_chastore {
-	chanode_t *head, *tail;
 	int isize, nsize;
 	chanode_t *ancur;
-	chanode_t *sncur;
-	int scurr;
 } chastore_t;
 
 static void cha_init(chastore_t *cha, int isize, int icount)
 {
-	cha->head = cha->tail = NULL;
 	cha->isize = isize;
 	cha->nsize = icount * isize;
-	cha->ancur = cha->sncur = NULL;
-	cha->scurr = 0;
+	cha->ancur = NULL;
 }
 
 static void *cha_alloc(chastore_t *cha)
@@ -111,12 +106,7 @@ static void *cha_alloc(chastore_t *cha)
 		if (!ancur)
 			return NULL;
 		ancur->icurr = 0;
-		ancur->next = NULL;
-		if (cha->tail)
-			cha->tail->next = ancur;
-		if (!cha->head)
-			cha->head = ancur;
-		cha->tail = ancur;
+		ancur->next = cha->ancur;
 		cha->ancur = ancur;
 	}
 
@@ -127,7 +117,7 @@ static void *cha_alloc(chastore_t *cha)
 
 static void cha_free(chastore_t *cha)
 {
-	chanode_t *cur = cha->head;
+	chanode_t *cur = cha->ancur;
 	while (cur) {
 		chanode_t *tmp = cur;
 		cur = cur->next;
@@ -142,7 +132,6 @@ typedef struct s_bdrecord {
 } bdrecord_t;
 
 typedef struct s_bdfile {
-	const unsigned char *data, *top;
 	chastore_t cha;
 	unsigned int fphbits;
 	bdrecord_t **fphash;
@@ -152,7 +141,7 @@ static int delta_prepare(const unsigned char *buf, int bufsize, bdfile_t *bdf)
 {
 	unsigned int fphbits;
 	int i, hsize;
-	const unsigned char *base, *data, *top;
+	const unsigned char *data, *top;
 	bdrecord_t *brec;
 	bdrecord_t **fphash;
 
@@ -165,13 +154,12 @@ static int delta_prepare(const unsigned char *buf, int bufsize, bdfile_t *bdf)
 		fphash[i] = NULL;
 	cha_init(&bdf->cha, sizeof(bdrecord_t), hsize / 4 + 1);
 
-	bdf->data = data = base = buf;
-	bdf->top = top = buf + bufsize;
-	data += (bufsize / BLK_SIZE) * BLK_SIZE;
+	top = buf + bufsize;
+	data = buf + (bufsize / BLK_SIZE) * BLK_SIZE;
 	if (data == top)
 		data -= BLK_SIZE;
 
-	for ( ; data >= base; data -= BLK_SIZE) {
+	for ( ; data >= buf; data -= BLK_SIZE) {
 		brec = cha_alloc(&bdf->cha);
 		if (!brec) {
 			cha_free(&bdf->cha);
@@ -208,7 +196,7 @@ void *diff_delta(void *from_buf, unsigned long from_size,
 {
 	int i, outpos, outsize, inscnt, csize, msize, moff;
 	unsigned int fp;
-	const unsigned char *data, *top, *ptr1, *ptr2;
+	const unsigned char *ref_data, *ref_top, *data, *top, *ptr1, *ptr2;
 	unsigned char *out, *orig;
 	bdrecord_t *brec;
 	bdfile_t bdf;
@@ -224,6 +212,8 @@ void *diff_delta(void *from_buf, unsigned long from_size,
 		return NULL;
 	}
 
+	ref_data = from_buf;
+	ref_top = from_buf + from_size;
 	data = to_buf;
 	top = to_buf + to_size;
 
@@ -253,7 +243,7 @@ void *diff_delta(void *from_buf, unsigned long from_size,
 		i = HASH(fp, bdf.fphbits);
 		for (brec = bdf.fphash[i]; brec; brec = brec->next) {
 			if (brec->fp == fp) {
-				csize = bdf.top - brec->ptr;
+				csize = ref_top - brec->ptr;
 				if (csize > top - data)
 					csize = top - data;
 				for (ptr1 = brec->ptr, ptr2 = data; 
@@ -262,7 +252,7 @@ void *diff_delta(void *from_buf, unsigned long from_size,
 
 				csize = ptr1 - brec->ptr;
 				if (csize > msize) {
-					moff = brec->ptr - bdf.data;
+					moff = brec->ptr - ref_data;
 					msize = csize;
 					if (msize >= 0x10000) {
 						msize = 0x10000;

From 06d900cf28117ad5260335ea9ab7533f223320bf Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 15 Dec 2005 17:53:44 -0800
Subject: [PATCH 10/43] Sort globbed refname in show-branch.

"git show-branch bugs/*" shows all branches whose name match the
specified pattern, but in the order readdir() happened to
returned.  Sort them to make the output more predictable.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 show-branch.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/show-branch.c b/show-branch.c
index ab158eb7d08..c7422460be0 100644
--- a/show-branch.c
+++ b/show-branch.c
@@ -450,6 +450,8 @@ static void append_one_rev(const char *av)
 		if (saved_matches == ref_name_cnt &&
 		    ref_name_cnt < MAX_REVS)
 			error("no matching refs with %s", av);
+		if (saved_matches + 1 < ref_name_cnt)
+			sort_ref_range(saved_matches, ref_name_cnt);
 		return;
 	}
 	die("bad sha1 reference %s", av);

From ee34518d629331dadd58b1a75294369d679eda8b Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Fri, 16 Dec 2005 02:40:25 +0100
Subject: [PATCH 11/43] We do not like "HEAD" as a new branch name

This makes git-check-ref-format fail for "HEAD". Since the check is only
executed when creating refs, the existing symbolic ref is safe.

Otherwise these commands, most likely are pilot errors, would do
pretty funky stuff:

	git checkout -b HEAD
	git pull . other:HEAD

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/refs.c b/refs.c
index d2aec73edcb..b8fcb98dad9 100644
--- a/refs.c
+++ b/refs.c
@@ -345,6 +345,11 @@ int check_ref_format(const char *ref)
 		if (!ch) {
 			if (level < 2)
 				return -1; /* at least of form "heads/blah" */
+
+			/* do not allow ref name to end in "HEAD" */
+			if (cp - ref > 4 && !strcmp(cp - 4, "HEAD"))
+				return -1;
+
 			return 0;
 		}
 	}

From 06bf6ac4248e834a229027908d405f5e42ac96d7 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 15 Dec 2005 18:52:51 -0800
Subject: [PATCH 12/43] refs.c: off-by-one fix.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/refs.c b/refs.c
index b8fcb98dad9..69858e48cbb 100644
--- a/refs.c
+++ b/refs.c
@@ -346,8 +346,11 @@ int check_ref_format(const char *ref)
 			if (level < 2)
 				return -1; /* at least of form "heads/blah" */
 
-			/* do not allow ref name to end in "HEAD" */
-			if (cp - ref > 4 && !strcmp(cp - 4, "HEAD"))
+			/* Do not allow ref name to end in "HEAD"
+			 * Note that cp is poiting at one past NUL at the end.
+			 * i.e. cp[-1] = NUL.
+			 */
+			if (5 <= cp - ref && !strcmp(cp - 5, "HEAD"))
 				return -1;
 
 			return 0;

From 3ae854c3567dd10055fbe12b8bd91bd2d447f55f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 16 Dec 2005 18:23:33 -0800
Subject: [PATCH 13/43] Examples of resetting.

Morten Welinder says examples of resetting is really about
recovering from botched commit/pulls.  I agree that pointers
from commands that cause a reset to be needed in the first place
would be very helpful.

Also reset examples did not mention "pull/merge" cases.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-commit.txt |  4 ++++
 Documentation/git-merge.txt  |  5 +++++
 Documentation/git-pull.txt   |  5 +++++
 Documentation/git-reset.txt  | 33 +++++++++++++++++++++++++++++++++
 4 files changed, 47 insertions(+)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index b92cf483152..8b91f221fe0 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -66,6 +66,10 @@ OPTIONS
 	Update specified paths in the index file before committing.
 
 
+If you make a commit and then found a mistake immediately after
+that, you can recover from it with gitlink:git-reset[1].
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 0cac563d40f..4ce799b520b 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -37,6 +37,11 @@ include::merge-options.txt[]
 include::merge-strategies.txt[]
 
 
+If you tried a merge which resulted in a complex conflicts and
+would want to start over, you can recover with
+gitlink:git-reset[1].
+
+
 HOW MERGE WORKS
 ---------------
 
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index c65ca9a5300..3a7d3852257 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -104,6 +104,11 @@ merge the remote `origin` head into the current,
 local `master` branch.
 
 
+If you tried a pull which resulted in a complex conflicts and
+would want to start over, you can recover with
+gitlink:git-reset[1].
+
+
 SEE ALSO
 --------
 gitlink:git-fetch[1], gitlink:git-merge[1]
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 02048918bf7..c6a269b7ef5 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -111,6 +111,39 @@ remain there.
 changes still in the working tree.
 ------------
 
+Undo a merge or pull::
++
+------------
+$ git pull <1>
+Trying really trivial in-index merge...
+fatal: Merge requires file-level merging
+Nope.
+...
+Auto-merging nitfol
+CONFLICT (content): Merge conflict in nitfol
+Automatic merge failed/prevented; fix up by hand
+$ git reset --hard <2>
+
+<1> try to update from the upstream resulted in a lot of
+conflicts; you were not ready to spend a lot of time merging
+right now, so you decide to do that later.
+<2> "pull" has not made merge commit, so "git reset --hard"
+which is a synonym for "git reset --hard HEAD" clears the mess
+from the index file and the working tree.
+
+$ git pull . topic/branch <3>
+Updating from 41223... to 13134...
+Fast forward
+$ git reset --hard ORIG_HEAD <4>
+
+<3> merge a topic branch into the current branch, which resulted
+in a fast forward.
+<4> but you decided that the topic branch is not ready for public
+consumption yet.  "pull" or "merge" always leaves the original
+tip of the current branch in ORIG_HEAD, so resetting hard to it
+brings your index file and the working tree back to that state,
+and resets the tip of the branch to that commit.
+------------
 
 Author
 ------

From 68283999f8ae0e9286f8b7f199905b77d608cb80 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 15 Dec 2005 18:03:59 -0800
Subject: [PATCH 14/43] Forbid pattern maching characters in refnames.

by marking '?', '*', and '[' as bad_ref_char().

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-check-ref-format.txt | 8 +++++---
 refs.c                                 | 4 +++-
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index 636e9516b09..f7f84c644ec 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -26,13 +26,15 @@ imposes the following rules on how refs are named:
 
 . It cannot have ASCII control character (i.e. bytes whose
   values are lower than \040, or \177 `DEL`), space, tilde `~`,
-  caret `{caret}`, or colon `:` anywhere;
+  caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
+  or open bracket `[` anywhere;
 
 . It cannot end with a slash `/`.
 
 These rules makes it easy for shell script based tools to parse
-refnames, and also avoids ambiguities in certain refname
-expressions (see gitlink:git-rev-parse[1]).  Namely:
+refnames, pathname expansion by the shell when a refname is used
+unquoted (by mistake), and also avoids ambiguities in certain
+refname expressions (see gitlink:git-rev-parse[1]).  Namely:
 
 . double-dot `..` are often used as in `ref1..ref2`, and in some
   context this notation means `{caret}ref1 ref2` (i.e. not in
diff --git a/refs.c b/refs.c
index 69858e48cbb..7a2c56e64b8 100644
--- a/refs.c
+++ b/refs.c
@@ -313,7 +313,9 @@ int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
 static inline int bad_ref_char(int ch)
 {
 	return (((unsigned) ch) <= ' ' ||
-		ch == '~' || ch == '^' || ch == ':');
+		ch == '~' || ch == '^' || ch == ':' ||
+		/* 2.13 Pattern Matching Notation */
+		ch == '?' || ch == '*' || ch == '[');
 }
 
 int check_ref_format(const char *ref)

From 69224716f78d6a772318e73060a70ba5ae67e55b Mon Sep 17 00:00:00 2001
From: Jan Harkes <jaharkes@cs.cmu.edu>
Date: Sat, 17 Dec 2005 01:01:06 -0500
Subject: [PATCH 15/43] Fix git-am --skip

git-am --skip does not unpack the next patch and ends up reapplying the
old patch, believing that it is the new patch in the sequence.

If the old patch applied successfully it will commit it with the
supposedly skipped log message and ends up dropping the following patch.
If the patch did not apply the user is left with the conflict he tried
to skip and has to unpack the next patch in the sequence by hand to get
git-am back on track.

By clearing the resume variable whenever skips bumps the sequence
counter we correctly unpack the next patch. I also added another
resume= in the case a patch file is missing from the sequence to
avoid the same problem when a file in the sequence was removed.

Signed-off-by: Jan Harkes <jaharkes@cs.cmu.edu>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-am.sh | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/git-am.sh b/git-am.sh
index 1a114bcc080..731ab1fffa4 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -211,6 +211,7 @@ this=`cat "$dotest/next"`
 if test "$skip" = t
 then
 	this=`expr "$this" + 1`
+	resume=
 fi
 
 if test "$this" -gt "$last"
@@ -225,6 +226,7 @@ do
 	msgnum=`printf "%0${prec}d" $this`
 	next=`expr "$this" + 1`
 	test -f "$dotest/$msgnum" || {
+		resume=
 		go_next
 		continue
 	}

From 01385e275828c1116ea9bfcf827f82f450ee8f5f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 16 Dec 2005 23:12:33 -0800
Subject: [PATCH 16/43] Comment fixes.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-branch.sh   | 4 ++--
 git-checkout.sh | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/git-branch.sh b/git-branch.sh
index 0266f46223c..b0e54ed2af9 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -32,11 +32,11 @@ delete_branch () {
 	    case " $mbs " in
 	    *' '$branch' '*)
 		# the merge base of branch and HEAD contains branch --
-		# which means that the HEAD contains everything in the HEAD.
+		# which means that the HEAD contains everything in both.
 		;;
 	    *)
 		echo >&2 "The branch '$branch_name' is not a strict subset of your current HEAD.
-    If you are sure you want to delete it, run 'git branch -D $branch_name'."
+If you are sure you want to delete it, run 'git branch -D $branch_name'."
 		exit 1
 		;;
 	    esac
diff --git a/git-checkout.sh b/git-checkout.sh
index f241d4ba6ba..36308d22c6a 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -116,7 +116,7 @@ else
 fi
 
 # 
-# Switch the HEAD pointer to the new branch if it we
+# Switch the HEAD pointer to the new branch if we
 # checked out a branch head, and remove any potential
 # old MERGE_HEAD's (subsequent commits will clearly not
 # be based on them, since we re-set the index)

From 80248b2e4888a05adcb903f53af84ee513d22245 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 17 Dec 2005 11:39:39 -0800
Subject: [PATCH 17/43] Documentation: HTTP needs update-server-info.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/everyday.txt               | 11 +++++++++++
 Documentation/git-update-server-info.txt |  6 +++---
 Documentation/tutorial.txt               | 18 +++++++++++-------
 3 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index d8d7a6441a8..df1e287999d 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -424,3 +424,14 @@ for branch policy control.
 david is the release manager and is the only person who can
 create and push version tags.
 ------------
+
+HTTP server to support dumb protocol transfer.::
++
+------------
+dev$ git update-server-info <1>
+dev$ ftp user@isp.example.com <2>
+ftp> cp -r .git /home/user/myproject.git
+
+<1> make sure your info/refs and objects/info/packs are up-to-date
+<2> upload to public HTTP server hosted by your ISP.
+------------
diff --git a/Documentation/git-update-server-info.txt b/Documentation/git-update-server-info.txt
index 527fb303ebc..88a03c7c5ed 100644
--- a/Documentation/git-update-server-info.txt
+++ b/Documentation/git-update-server-info.txt
@@ -12,11 +12,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-A dumb server that does not do on-the-fly pack generations can
+A dumb server that does not do on-the-fly pack generations must
 have some auxiliary information files in $GIT_DIR/info and
 $GIT_OBJECT_DIRECTORY/info directories to help clients discover
-what references and packs the server has and make optimized
-pull decisions.  This command generates such auxiliary files.
+what references and packs the server has.  This command
+generates such auxiliary files.
 
 
 OPTIONS
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 543afb84e50..1683f0bc2db 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -1091,9 +1091,10 @@ lacks and transfers (close to) minimum set of objects.
 HTTP(S)::
 	`http://remote.machine/path/to/repo.git/`
 +
-HTTP and HTTPS transport are used only for downloading.  They
-first obtain the topmost commit object name from the remote site
-by looking at `repo.git/info/refs` file, tries to obtain the
+Downloader from http and https URL
+first obtains the topmost commit object name from the remote site
+by looking at the specified refname under `repo.git/refs/` directory,
+and then tries to obtain the
 commit object by downloading from `repo.git/objects/xx/xxx\...`
 using the object name of that commit object.  Then it reads the
 commit object to find out its parent commits and the associate
@@ -1104,7 +1105,9 @@ sometimes also called 'commit walkers'.
 The 'commit walkers' are sometimes also called 'dumb
 transports', because they do not require any git aware smart
 server like git Native transport does.  Any stock HTTP server
-would suffice.
+that does not even support directory index would suffice.  But
+you must prepare your repository with `git-update-server-info`
+to help dumb transport downloaders.
 +
 There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
 programs, which are 'commit walkers'; they outlived their
@@ -1517,12 +1520,13 @@ A recommended workflow for a "project lead" goes like this:
 2. Prepare a public repository accessible to others.
 +
 If other people are pulling from your repository over dumb
-transport protocols, you need to keep this repository 'dumb
-transport friendly'.  After `git init-db`,
+transport protocols (HTTP), you need to keep this repository
+'dumb transport friendly'.  After `git init-db`,
 `$GIT_DIR/hooks/post-update` copied from the standard templates
 would contain a call to `git-update-server-info` but the
 `post-update` hook itself is disabled by default -- enable it
-with `chmod +x post-update`.
+with `chmod +x post-update`.  This makes sure `git-update-server-info`
+keeps the necessary files up-to-date.
 
 3. Push into the public repository from your primary
    repository.

From 011fbc7f07fdf82cf922d1eb269bb4dac3e0cc40 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 16 Dec 2005 23:19:14 -0800
Subject: [PATCH 18/43] Remove misguided branch disambiguation.

This removes the misguided attempt to refuse processing a branch
name xyzzy and insist it to be given as either heads/xyzzy or
tags/xyzzy when a tag xyzzy exists.  There was no reason to do
so --- the search order was predictable and well defined, so if
the user says xyzzy we should have taken the tag xyzzy in such a
case without complaining.

This incidentally fixes another subtle bug related to this.  If
such a duplicate branch/tag name happened to be a unique valid
prefix of an existing commit object name (say, "beef"), we did
not take the tag "beef" but after complaining used the commit
object whose name started with beef.

Another problem this fixes while introducing some confusion is
that there is no longer a reason to forbid a branch name HEAD
anymore.  In other words, now "git pull . ref1:HEAD" would work
as expected, once we revert "We do not like HEAD branch" patch.
It creates "HEAD" branch under ${GIT_DIR-.git}/refs/heads (or
fast-forwards if already exists) using the tip of ref1 branch
from the current repository, and merges it into the current
branch.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 sha1_name.c | 33 +++------------------------------
 1 file changed, 3 insertions(+), 30 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index bf8f0f0e1fe..49e2cc394f2 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -238,7 +238,6 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 		NULL
 	};
 	const char **p;
-	int found = 0;
 
 	if (len == 40 && !get_sha1_hex(str, sha1))
 		return 0;
@@ -249,36 +248,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 
 	for (p = prefix; *p; p++) {
 		char *pathname = git_path("%s/%.*s", *p, len, str);
-
-		if (!read_ref(pathname, sha1)) {
-			/* Must be unique; i.e. when heads/foo and
-			 * tags/foo are both present, reject "foo".
-			 */
-			if (1 < found++)
-				return -1;
-		}
-
-		/* We want to allow .git/description file and
-		 * "description" branch to exist at the same time.
-		 * "git-rev-parse description" should silently skip
-		 * .git/description file as a candidate for
-		 * get_sha1().  However, having garbage file anywhere
-		 * under refs/ is not OK, and we would not have caught
-		 * ambiguous heads and tags with the above test.
-		 */
-		else if (**p && !access(pathname, F_OK)) {
-			/* Garbage exists under .git/refs */
-			return error("garbage ref found '%s'", pathname);
-		}
-	}
-	switch (found) {
-	case 0:
-		return -1;
-	case 1:
-		return 0;
-	default:
-		return error("ambiguous refname '%.*s'", len, str);
+		if (!read_ref(pathname, sha1))
+			return 0;
 	}
+	return -1;
 }
 
 static int get_sha1_1(const char *name, int len, unsigned char *sha1);

From 8872f27b8773dfe7d3dbb440c27d4a5a9dbc8470 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 16 Dec 2005 23:48:14 -0800
Subject: [PATCH 19/43] Revert "refs.c: off-by-one fix."

This reverts 06bf6ac4248e834a229027908d405f5e42ac96d7 commit.
---
 refs.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/refs.c b/refs.c
index 7a2c56e64b8..0d63c1fcab9 100644
--- a/refs.c
+++ b/refs.c
@@ -348,11 +348,8 @@ int check_ref_format(const char *ref)
 			if (level < 2)
 				return -1; /* at least of form "heads/blah" */
 
-			/* Do not allow ref name to end in "HEAD"
-			 * Note that cp is poiting at one past NUL at the end.
-			 * i.e. cp[-1] = NUL.
-			 */
-			if (5 <= cp - ref && !strcmp(cp - 5, "HEAD"))
+			/* do not allow ref name to end in "HEAD" */
+			if (cp - ref > 4 && !strcmp(cp - 4, "HEAD"))
 				return -1;
 
 			return 0;

From f7087e2e7c34421b4577876969da94d0f6195414 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 16 Dec 2005 23:48:22 -0800
Subject: [PATCH 20/43] Revert "We do not like "HEAD" as a new branch name"

This reverts ee34518d629331dadd58b1a75294369d679eda8b commit.
---
 refs.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/refs.c b/refs.c
index 0d63c1fcab9..c33729c54a0 100644
--- a/refs.c
+++ b/refs.c
@@ -347,11 +347,6 @@ int check_ref_format(const char *ref)
 		if (!ch) {
 			if (level < 2)
 				return -1; /* at least of form "heads/blah" */
-
-			/* do not allow ref name to end in "HEAD" */
-			if (cp - ref > 4 && !strcmp(cp - 4, "HEAD"))
-				return -1;
-
 			return 0;
 		}
 	}

From c054d64e8783e5ac2fa68c382f00df9087bca0f9 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 17 Dec 2005 00:00:50 -0800
Subject: [PATCH 21/43] Revert "get_sha1_basic(): corner case ambiguity fix"

This reverts 6677c4665af2d73f670bec382bc82d0f2e9513fb commit.

The misguided disambiguation has been reverted, so there is no point
testing that misfeature.
---
 sha1_name.c      |  2 +-
 t/t0000-basic.sh | 48 ------------------------------------------------
 2 files changed, 1 insertion(+), 49 deletions(-)

diff --git a/sha1_name.c b/sha1_name.c
index 49e2cc394f2..67b69a54fb7 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -223,7 +223,7 @@ static int ambiguous_path(const char *path, int len)
 			slash = 0;
 			continue;
 		}
-		return slash;
+		break;
 	}
 	return slash;
 }
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index ffa723ea8ba..bc3e711a525 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -205,52 +205,4 @@ test_expect_success \
     'no diff after checkout and git-update-index --refresh.' \
     'git-diff-files >current && cmp -s current /dev/null'
 
-
-# extended sha1 parsing and ambiguity resolution
-
-GIT_AUTHOR_DATE='1995-01-29T16:00:00 -0800'
-GIT_AUTHOR_EMAIL=a.u.thor@example.com
-GIT_AUTHOR_NAME='A U Thor'
-GIT_COMMITTER_DATE='1995-01-29T16:00:00 -0800'
-GIT_COMMITTER_EMAIL=c.o.mmitter@example.com
-GIT_COMMITTER_NAME='C O Mmitter'
-export GIT_AUTHOR_DATE
-export GIT_AUTHOR_EMAIL
-export GIT_AUTHOR_NAME
-export GIT_COMMITTER_DATE
-export GIT_COMMITTER_EMAIL
-export GIT_COMMITTER_NAME
-
-test_expect_success \
-	'initial commit.' \
-	'commit=$(echo Initial commit | git-commit-tree $tree) &&
-	 echo "$commit" >.git/refs/heads/master &&
-	 git-ls-tree HEAD &&
-	 test "$commit" = 51a092e9ef6cbbe66d258acd17599d3f80be6162'
-
-test_expect_success \
-	'Ambiguous' \
-	'echo "$commit" >.git/refs/heads/nasty &&
-	 echo "$commit" >.git/refs/tags/nasty &&
-	 if git-rev-parse --verify nasty
-	 then
-		echo "should have barfed"
-		false
-	 else
-	 	:
-	 fi &&
-	 # names directly underneath .git/ should not interfere
-	 echo "$commit" >.git/refs/heads/description &&
-	 git-rev-parse --verify description &&
-	 # broken object name
-	 echo fffffffffffffffffffffffffffffffffffffffg \
-	 	>.git/refs/heads/nasty &&
-	 if git-rev-parse --verify nasty
-	 then
-		echo "should have barfed"
-		false
-	 else
-	 	:
-	 fi'
-
 test_done

From ad8972150887a8ed3dd4869fc9318cc2e48dd69f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 14 Dec 2005 22:17:38 -0800
Subject: [PATCH 22/43] fetch-pack: -k option to keep downloaded pack.

Split out the functions that deal with the socketpair after
finishing git protocol handshake to receive the packed data into
a separate file, and use it in fetch-pack to keep/explode the
received pack data.  We earlier had something like that on
clone-pack side once, but the list discussion resulted in the
decision that it makes sense to always keep the pack for
clone-pack, so unpacking option is not enabled on the clone-pack
side, but we later still could do so easily if we wanted to with
this change.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-fetch-pack.txt |   7 +-
 Makefile                         |   1 +
 cache.h                          |   5 +
 clone-pack.c                     | 136 +-----------------------
 fetch-clone.c                    | 172 +++++++++++++++++++++++++++++++
 fetch-pack.c                     |  58 ++++-------
 6 files changed, 206 insertions(+), 173 deletions(-)
 create mode 100644 fetch-clone.c

diff --git a/Documentation/git-fetch-pack.txt b/Documentation/git-fetch-pack.txt
index ea6faab0598..b507e9b6486 100644
--- a/Documentation/git-fetch-pack.txt
+++ b/Documentation/git-fetch-pack.txt
@@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository.
 
 SYNOPSIS
 --------
-git-fetch-pack [-q] [--exec=<git-upload-pack>] [<host>:]<directory> [<refs>...]
+git-fetch-pack [-q] [-k] [--exec=<git-upload-pack>] [<host>:]<directory> [<refs>...]
 
 DESCRIPTION
 -----------
@@ -29,6 +29,11 @@ OPTIONS
 	Pass '-q' flag to 'git-unpack-objects'; this makes the
 	cloning process less verbose.
 
+-k::
+	Do not invoke 'git-unpack-objects' on received data, but
+	create a single packfile out of it instead, and store it
+	in the object database.
+
 --exec=<git-upload-pack>::
 	Use this to specify the path to 'git-upload-pack' on the
 	remote side, if is not found on your $PATH.
diff --git a/Makefile b/Makefile
index d494ad4b1fb..9fd2ed3d92e 100644
--- a/Makefile
+++ b/Makefile
@@ -175,6 +175,7 @@ LIB_OBJS = \
 	quote.o read-cache.o refs.o run-command.o \
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
+	fetch-clone.o \
 	$(DIFF_OBJS)
 
 LIBS = $(LIB_FILE)
diff --git a/cache.h b/cache.h
index c78d8aea415..cb87becb3aa 100644
--- a/cache.h
+++ b/cache.h
@@ -338,4 +338,9 @@ extern char git_default_name[MAX_GITNAME];
 extern char git_commit_encoding[MAX_ENCODING_LENGTH];
 
 extern int copy_fd(int ifd, int ofd);
+
+/* Finish off pack transfer receiving end */
+extern int receive_unpack_pack(int fd[2], const char *me, int quiet);
+extern int receive_keep_pack(int fd[2], const char *me);
+
 #endif /* CACHE_H */
diff --git a/clone-pack.c b/clone-pack.c
index b5ce5d31115..03dbc2ead6e 100644
--- a/clone-pack.c
+++ b/clone-pack.c
@@ -1,7 +1,6 @@
 #include "cache.h"
 #include "refs.h"
 #include "pkt-line.h"
-#include <sys/wait.h>
 
 static const char clone_pack_usage[] =
 "git-clone-pack [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*";
@@ -112,139 +111,6 @@ static void write_refs(struct ref *ref)
 	free(head_path);
 }
 
-static int finish_pack(const char *pack_tmp_name)
-{
-	int pipe_fd[2];
-	pid_t pid;
-	char idx[PATH_MAX];
-	char final[PATH_MAX];
-	char hash[41];
-	unsigned char sha1[20];
-	char *cp;
-	int err = 0;
-
-	if (pipe(pipe_fd) < 0)
-		die("git-clone-pack: unable to set up pipe");
-
-	strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
-	cp = strrchr(idx, '/');
-	memcpy(cp, "/pidx", 5);
-
-	pid = fork();
-	if (pid < 0)
-		die("git-clone-pack: unable to fork off git-index-pack");
-	if (!pid) {
-		close(0);
-		dup2(pipe_fd[1], 1);
-		close(pipe_fd[0]);
-		close(pipe_fd[1]);
-		execlp("git-index-pack","git-index-pack",
-		       "-o", idx, pack_tmp_name, NULL);
-		error("cannot exec git-index-pack <%s> <%s>",
-		      idx, pack_tmp_name);
-		exit(1);
-	}
-	close(pipe_fd[1]);
-	if (read(pipe_fd[0], hash, 40) != 40) {
-		error("git-clone-pack: unable to read from git-index-pack");
-		err = 1;
-	}
-	close(pipe_fd[0]);
-
-	for (;;) {
-		int status, code;
-		int retval = waitpid(pid, &status, 0);
-
-		if (retval < 0) {
-			if (errno == EINTR)
-				continue;
-			error("waitpid failed (%s)", strerror(retval));
-			goto error_die;
-		}
-		if (WIFSIGNALED(status)) {
-			int sig = WTERMSIG(status);
-			error("git-index-pack died of signal %d", sig);
-			goto error_die;
-		}
-		if (!WIFEXITED(status)) {
-			error("git-index-pack died of unnatural causes %d",
-			      status);
-			goto error_die;
-		}
-		code = WEXITSTATUS(status);
-		if (code) {
-			error("git-index-pack died with error code %d", code);
-			goto error_die;
-		}
-		if (err)
-			goto error_die;
-		break;
-	}
-	hash[40] = 0;
-	if (get_sha1_hex(hash, sha1)) {
-		error("git-index-pack reported nonsense '%s'", hash);
-		goto error_die;
-	}
-	/* Now we have pack in pack_tmp_name[], and
-	 * idx in idx[]; rename them to their final names.
-	 */
-	snprintf(final, sizeof(final),
-		 "%s/pack/pack-%s.pack", get_object_directory(), hash);
-	move_temp_to_file(pack_tmp_name, final);
-	chmod(final, 0444);
-	snprintf(final, sizeof(final),
-		 "%s/pack/pack-%s.idx", get_object_directory(), hash);
-	move_temp_to_file(idx, final);
-	chmod(final, 0444);
-	return 0;
-
- error_die:
-	unlink(idx);
-	unlink(pack_tmp_name);
-	exit(1);
-}
-
-static int clone_without_unpack(int fd[2])
-{
-	char tmpfile[PATH_MAX];
-	int ofd, ifd;
-
-	ifd = fd[0];
-	snprintf(tmpfile, sizeof(tmpfile),
-		 "%s/pack/tmp-XXXXXX", get_object_directory());
-	ofd = mkstemp(tmpfile);
-	if (ofd < 0)
-		return error("unable to create temporary file %s", tmpfile);
-
-	while (1) {
-		char buf[8192];
-		ssize_t sz, wsz, pos;
-		sz = read(ifd, buf, sizeof(buf));
-		if (sz == 0)
-			break;
-		if (sz < 0) {
-			error("error reading pack (%s)", strerror(errno));
-			close(ofd);
-			unlink(tmpfile);
-			return -1;
-		}
-		pos = 0;
-		while (pos < sz) {
-			wsz = write(ofd, buf + pos, sz - pos);
-			if (wsz < 0) {
-				error("error writing pack (%s)",
-				      strerror(errno));
-				close(ofd);
-				unlink(tmpfile);
-				return -1;
-			}
-			pos += wsz;
-		}
-	}
-	close(ofd);
-	return finish_pack(tmpfile);
-}
-
 static int clone_pack(int fd[2], int nr_match, char **match)
 {
 	struct ref *refs;
@@ -257,7 +123,7 @@ static int clone_pack(int fd[2], int nr_match, char **match)
 	}
 	clone_handshake(fd, refs);
 
-	status = clone_without_unpack(fd);
+	status = receive_keep_pack(fd, "git-clone-pack");
 
 	if (!status) {
 		if (nr_match == 0)
diff --git a/fetch-clone.c b/fetch-clone.c
new file mode 100644
index 00000000000..2b2aa15ea77
--- /dev/null
+++ b/fetch-clone.c
@@ -0,0 +1,172 @@
+#include "cache.h"
+#include <sys/wait.h>
+
+static int finish_pack(const char *pack_tmp_name, const char *me)
+{
+	int pipe_fd[2];
+	pid_t pid;
+	char idx[PATH_MAX];
+	char final[PATH_MAX];
+	char hash[41];
+	unsigned char sha1[20];
+	char *cp;
+	int err = 0;
+
+	if (pipe(pipe_fd) < 0)
+		die("%s: unable to set up pipe", me);
+
+	strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
+	cp = strrchr(idx, '/');
+	memcpy(cp, "/pidx", 5);
+
+	pid = fork();
+	if (pid < 0)
+		die("git-clone-pack: unable to fork off git-index-pack");
+	if (!pid) {
+		close(0);
+		dup2(pipe_fd[1], 1);
+		close(pipe_fd[0]);
+		close(pipe_fd[1]);
+		execlp("git-index-pack","git-index-pack",
+		       "-o", idx, pack_tmp_name, NULL);
+		error("cannot exec git-index-pack <%s> <%s>",
+		      idx, pack_tmp_name);
+		exit(1);
+	}
+	close(pipe_fd[1]);
+	if (read(pipe_fd[0], hash, 40) != 40) {
+		error("%s: unable to read from git-index-pack", me);
+		err = 1;
+	}
+	close(pipe_fd[0]);
+
+	for (;;) {
+		int status, code;
+		int retval = waitpid(pid, &status, 0);
+
+		if (retval < 0) {
+			if (errno == EINTR)
+				continue;
+			error("waitpid failed (%s)", strerror(retval));
+			goto error_die;
+		}
+		if (WIFSIGNALED(status)) {
+			int sig = WTERMSIG(status);
+			error("git-index-pack died of signal %d", sig);
+			goto error_die;
+		}
+		if (!WIFEXITED(status)) {
+			error("git-index-pack died of unnatural causes %d",
+			      status);
+			goto error_die;
+		}
+		code = WEXITSTATUS(status);
+		if (code) {
+			error("git-index-pack died with error code %d", code);
+			goto error_die;
+		}
+		if (err)
+			goto error_die;
+		break;
+	}
+	hash[40] = 0;
+	if (get_sha1_hex(hash, sha1)) {
+		error("git-index-pack reported nonsense '%s'", hash);
+		goto error_die;
+	}
+	/* Now we have pack in pack_tmp_name[], and
+	 * idx in idx[]; rename them to their final names.
+	 */
+	snprintf(final, sizeof(final),
+		 "%s/pack/pack-%s.pack", get_object_directory(), hash);
+	move_temp_to_file(pack_tmp_name, final);
+	chmod(final, 0444);
+	snprintf(final, sizeof(final),
+		 "%s/pack/pack-%s.idx", get_object_directory(), hash);
+	move_temp_to_file(idx, final);
+	chmod(final, 0444);
+	return 0;
+
+ error_die:
+	unlink(idx);
+	unlink(pack_tmp_name);
+	exit(1);
+}
+
+int receive_unpack_pack(int fd[2], const char *me, int quiet)
+{
+	int status;
+	pid_t pid;
+
+	pid = fork();
+	if (pid < 0)
+		die("%s: unable to fork off git-unpack-objects", me);
+	if (!pid) {
+		dup2(fd[0], 0);
+		close(fd[0]);
+		close(fd[1]);
+		execlp("git-unpack-objects", "git-unpack-objects",
+		       quiet ? "-q" : NULL, NULL);
+		die("git-unpack-objects exec failed");
+	}
+	close(fd[0]);
+	close(fd[1]);
+	while (waitpid(pid, &status, 0) < 0) {
+		if (errno != EINTR)
+			die("waiting for git-unpack-objects: %s",
+			    strerror(errno));
+	}
+	if (WIFEXITED(status)) {
+		int code = WEXITSTATUS(status);
+		if (code)
+			die("git-unpack-objects died with error code %d",
+			    code);
+		return 0;
+	}
+	if (WIFSIGNALED(status)) {
+		int sig = WTERMSIG(status);
+		die("git-unpack-objects died of signal %d", sig);
+	}
+	die("git-unpack-objects died of unnatural causes %d", status);
+}
+
+int receive_keep_pack(int fd[2], const char *me)
+{
+	char tmpfile[PATH_MAX];
+	int ofd, ifd;
+
+	ifd = fd[0];
+	snprintf(tmpfile, sizeof(tmpfile),
+		 "%s/pack/tmp-XXXXXX", get_object_directory());
+	ofd = mkstemp(tmpfile);
+	if (ofd < 0)
+		return error("unable to create temporary file %s", tmpfile);
+
+	while (1) {
+		char buf[8192];
+		ssize_t sz, wsz, pos;
+		sz = read(ifd, buf, sizeof(buf));
+		if (sz == 0)
+			break;
+		if (sz < 0) {
+			error("error reading pack (%s)", strerror(errno));
+			close(ofd);
+			unlink(tmpfile);
+			return -1;
+		}
+		pos = 0;
+		while (pos < sz) {
+			wsz = write(ofd, buf + pos, sz - pos);
+			if (wsz < 0) {
+				error("error writing pack (%s)",
+				      strerror(errno));
+				close(ofd);
+				unlink(tmpfile);
+				return -1;
+			}
+			pos += wsz;
+		}
+	}
+	close(ofd);
+	return finish_pack(tmpfile, me);
+}
diff --git a/fetch-pack.c b/fetch-pack.c
index 58ba2094dc6..2528053fa81 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -3,13 +3,12 @@
 #include "pkt-line.h"
 #include "commit.h"
 #include "tag.h"
-#include <time.h>
-#include <sys/wait.h>
 
+static int keep_pack;
 static int quiet;
 static int verbose;
 static const char fetch_pack_usage[] =
-"git-fetch-pack [-q] [-v] [--exec=upload-pack] [host:]directory <refs>...";
+"git-fetch-pack [-q] [-v] [-k] [--exec=upload-pack] [host:]directory <refs>...";
 static const char *exec = "git-upload-pack";
 
 #define COMPLETE	(1U << 0)
@@ -363,7 +362,6 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
 	struct ref *ref;
 	unsigned char sha1[20];
 	int status;
-	pid_t pid;
 
 	get_remote_heads(fd[0], &ref, 0, NULL, 0);
 	if (server_supports("multi_ack")) {
@@ -381,40 +379,22 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
 	}
 	if (find_common(fd, sha1, ref) < 0)
 		fprintf(stderr, "warning: no common commits\n");
-	pid = fork();
-	if (pid < 0)
-		die("git-fetch-pack: unable to fork off git-unpack-objects");
-	if (!pid) {
-		dup2(fd[0], 0);
-		close(fd[0]);
-		close(fd[1]);
-		execlp("git-unpack-objects", "git-unpack-objects",
-		       quiet ? "-q" : NULL, NULL);
-		die("git-unpack-objects exec failed");
+
+	if (keep_pack)
+		status = receive_keep_pack(fd, "git-fetch-pack");
+	else
+		status = receive_unpack_pack(fd, "git-fetch-pack", quiet);
+
+	if (status)
+		die("git-fetch-pack: fetch failed.");
+
+ all_done:
+	while (ref) {
+		printf("%s %s\n",
+		       sha1_to_hex(ref->old_sha1), ref->name);
+		ref = ref->next;
 	}
-	close(fd[0]);
-	close(fd[1]);
-	while (waitpid(pid, &status, 0) < 0) {
-		if (errno != EINTR)
-			die("waiting for git-unpack-objects: %s", strerror(errno));
-	}
-	if (WIFEXITED(status)) {
-		int code = WEXITSTATUS(status);
-		if (code)
-			die("git-unpack-objects died with error code %d", code);
-all_done:
-		while (ref) {
-			printf("%s %s\n",
-			       sha1_to_hex(ref->old_sha1), ref->name);
-			ref = ref->next;
-		}
-		return 0;
-	}
-	if (WIFSIGNALED(status)) {
-		int sig = WTERMSIG(status);
-		die("git-unpack-objects died of signal %d", sig);
-	}
-	die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
+	return 0;
 }
 
 int main(int argc, char **argv)
@@ -440,6 +420,10 @@ int main(int argc, char **argv)
 				quiet = 1;
 				continue;
 			}
+			if (!strcmp("-k", arg)) {
+				keep_pack = 1;
+				continue;
+			}
 			if (!strcmp("-v", arg)) {
 				verbose = 1;
 				continue;

From 2247efb40b304f158b9ab41455bd711366a44f57 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 18 Dec 2005 01:55:29 -0800
Subject: [PATCH 23/43] clone-pack: remove unused and undocumented --keep flag

While we are at it, give fully spelled --keep to fetch-pack.
Also give --quiet in addition to -q to fetch-pack as well.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 clone-pack.c | 2 --
 fetch-pack.c | 4 ++--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/clone-pack.c b/clone-pack.c
index 03dbc2ead6e..f634431be1f 100644
--- a/clone-pack.c
+++ b/clone-pack.c
@@ -160,8 +160,6 @@ int main(int argc, char **argv)
 				exec = arg + 7;
 				continue;
 			}
-			if (!strcmp("--keep", arg))
-				continue;
 			usage(clone_pack_usage);
 		}
 		dest = arg;
diff --git a/fetch-pack.c b/fetch-pack.c
index 2528053fa81..d34f322477a 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -416,11 +416,11 @@ int main(int argc, char **argv)
 				exec = arg + 7;
 				continue;
 			}
-			if (!strcmp("-q", arg)) {
+			if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) {
 				quiet = 1;
 				continue;
 			}
-			if (!strcmp("-k", arg)) {
+			if (!strcmp("--keep", arg) || !strcmp("-k", arg)) {
 				keep_pack = 1;
 				continue;
 			}

From d808111ebdb0b50709527612221eb2970ed6ece9 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 18 Dec 2005 12:11:27 -0800
Subject: [PATCH 24/43] Documentation: typos and small fixes in "everyday".

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/everyday.txt | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index df1e287999d..3ab9b916c29 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -211,10 +211,12 @@ $ git fetch --tags <8>
 
 <1> repeat as needed.
 <2> extract patches from your branch for e-mail submission.
-<3> "pull" fetches from "origin" by default and merges.
-<4> look at the changes since last time we checked, only in the
+<3> "pull" fetches from "origin" by default and merges into the
+current branch.
+<4> immediately after pulling, look at the changes done upstream
+since last time we checked, only in the
 area we are interested in.
-<5> fetch from a specific branch from a specific repository and and merge.
+<5> fetch from a specific branch from a specific repository and merge.
 <6> revert the pull.
 <7> garbage collect leftover objects from reverted pull.
 <8> from time to time, obtain official tags from the "origin"
@@ -330,16 +332,18 @@ master, nor exposed as a part of a stable branch.
 <8> and bundle topic branches still cooking.
 <9> backport a critical fix.
 <10> create a signed tag.
-<11> make sure I did not accidentally rewound master beyond what I
+<11> make sure I did not accidentally rewind master beyond what I
 already pushed out.  "ko" shorthand points at the repository I have
 at kernel.org, and looks like this:
-$ cat .git/remotes/ko
-URL: kernel.org:/pub/scm/git/git.git
-Pull: master:refs/tags/ko-master
-Pull: maint:refs/tags/ko-maint
-Push: master
-Push: +pu
-Push: maint
+    $ cat .git/remotes/ko
+    URL: kernel.org:/pub/scm/git/git.git
+    Pull: master:refs/tags/ko-master
+    Pull: maint:refs/tags/ko-maint
+    Push: master
+    Push: +pu
+    Push: maint
+In the output from "git show-branch", "master" should have
+everything "ko-master" has.
 <12> push out the bleeding edge.
 <13> push the tag out, too.
 ------------
@@ -357,8 +361,8 @@ and maintain access to the repository by developers.
   * gitlink:git-shell[1] can be used as a 'restricted login shell'
     for shared central repository users.
 
-  * link:howto/update-hook-example.txt[update hook howto] has a
-    good example of managing a shared central repository.
+link:howto/update-hook-example.txt[update hook howto] has a good
+example of managing a shared central repository.
 
 
 Examples

From ea77e675e564211513ebedb4f5bdcda482d7fd30 Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Sun, 18 Dec 2005 12:15:58 -0800
Subject: [PATCH 25/43] Make "git help" react to window size correctly

Currently the git "show commands" function will react to the environment
variable COLUMNS, or just default to a width of 80 characters.

That's just soo eighties. Nobody sane sets COLUMNS any more, unless they
need to support some stone-age software from before the age of steam
engines, SIGWINCH and TIOCGWINSZ.

So get with the new century, and use TIOCGWINSZ to get the terminal size.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/git.c b/git.c
index c26cac6555e..157c5491442 100644
--- a/git.c
+++ b/git.c
@@ -8,6 +8,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <sys/ioctl.h>
 #include "git-compat-util.h"
 
 #ifndef PATH_MAX
@@ -26,6 +27,16 @@ static int term_columns(void)
 	if (col_string && (n_cols = atoi(col_string)) > 0)
 		return n_cols;
 
+#ifdef TIOCGWINSZ
+	{
+		struct winsize ws;
+		if (!ioctl(1, TIOCGWINSZ, &ws)) {
+			if (ws.ws_col)
+				return ws.ws_col;
+		}
+	}
+#endif
+
 	return 80;
 }
 

From 112d0bafd620c0e0f10614a3ba021d4de4fae331 Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Sun, 18 Dec 2005 12:41:10 -0800
Subject: [PATCH 26/43] Make "git help" sort git commands in columns

This changes "pretty_print_string_list()" to show the git commands
alphabetically in column order, which is the normal one.

Ie instead of doing

	git commands available in '/home/torvalds/bin'
	----------------------------------------------
	  add                am                 ...
	  applypatch         archimport         ...
	  cat-file           check-ref-format   ...
	...

it does

	git commands available in '/home/torvalds/bin'
	----------------------------------------------
	  add                diff-tree          ...
	  am                 fetch              ...
	  apply              fetch-pack         ...
	...

where each column is sorted.

This is how "ls" sorts things too, and since visually the columns are much
more distinct than the rows, so it _looks_ more sorted.

The "ls" command has a "-x" option that lists entries by lines (the way
git.c used to): if somebody wants to do that, the new print-out logic
could be easily accomodated to that too. Matter of taste and preference, I
guess.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git.c | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/git.c b/git.c
index 157c5491442..0fd95bf751c 100644
--- a/git.c
+++ b/git.c
@@ -85,25 +85,28 @@ static int cmdname_compare(const void *a_, const void *b_)
 
 static void pretty_print_string_list(struct cmdname **cmdname, int longest)
 {
-	int cols = 1;
+	int cols = 1, rows;
 	int space = longest + 1; /* min 1 SP between words */
 	int max_cols = term_columns() - 1; /* don't print *on* the edge */
-	int i;
+	int i, j;
 
 	if (space < max_cols)
 		cols = max_cols / space;
+	rows = (cmdname_cnt + cols - 1) / cols;
 
 	qsort(cmdname, cmdname_cnt, sizeof(*cmdname), cmdname_compare);
 
-	for (i = 0; i < cmdname_cnt; ) {
-		int c;
+	for (i = 0; i < rows; i++) {
 		printf("  ");
 
-		for (c = cols; c && i < cmdname_cnt; i++) {
-			printf("%s", cmdname[i]->name);
-
-			if (--c)
-				mput_char(' ', space - cmdname[i]->len);
+		for (j = 0; j < cols; j++) {
+			int n = j * rows + i;
+			int size = space;
+			if (n >= cmdname_cnt)
+				break;
+			if (j == cols-1 || n + rows >= cmdname_cnt)
+				size = 1;
+			printf("%-*s", size, cmdname[n]->name);
 		}
 		putchar('\n');
 	}

From 3af849a3dae9442767668cd2045e360225724558 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 00:31:08 -0800
Subject: [PATCH 27/43] howto/using-topic-branches: Recommend public URL
 git://git.kernel.org/

Recommending this means subsystem maintainers do not have to log-in
just to resync with upstream.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/howto/using-topic-branches.txt | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/Documentation/howto/using-topic-branches.txt b/Documentation/howto/using-topic-branches.txt
index 494429738f8..b3d592fc3e1 100644
--- a/Documentation/howto/using-topic-branches.txt
+++ b/Documentation/howto/using-topic-branches.txt
@@ -42,8 +42,7 @@ So here is the step-by-step guide how this all works for me.
 
 First create your work tree by cloning Linus's public tree:
 
- $ git clone \
- master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
+ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
 
 Change directory into the cloned tree you just created
 
@@ -53,7 +52,7 @@ Set up a remotes file so that you can fetch the latest from Linus' master
 branch into a local branch named "linus":
 
  $ cat > .git/remotes/linus
- URL: master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
+ URL: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
  Pull: master:linus
  ^D
 

From 42f4570c86c41c5bfdc72465afc8f1928da69eea Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Sun, 18 Dec 2005 17:23:50 -0800
Subject: [PATCH 28/43] Documentation/git-archimport: document -o, -a, f, -D
 options

Also, ensure usage help switches are in the same order.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-archimport.txt | 23 ++++++++++++++++++++++-
 git-archimport.perl              |  5 +++--
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-archimport.txt b/Documentation/git-archimport.txt
index fcda0125af9..a2bd788f379 100644
--- a/Documentation/git-archimport.txt
+++ b/Documentation/git-archimport.txt
@@ -8,7 +8,8 @@ git-archimport - Import an Arch repository into git
 
 SYNOPSIS
 --------
-`git-archimport` [ -h ] [ -v ] [ -T ] [ -t tempdir ] 
+`git-archimport` [ -h ] [ -v ] [ -o ] [ -a ] [ -f ] [ -T ]
+                 [ -D depth ] [ -t tempdir ] 
                  <archive/branch> [ <archive/branch> ]
 
 DESCRIPTION
@@ -63,6 +64,26 @@ OPTIONS
 	Many tags. Will create a tag for every commit, reflecting the commit 
 	name in the Arch repository.
 
+-f::
+	Use the fast patchset import strategy.  This can be significantly
+	faster for large trees, but cannot handle directory renames or
+	permissions changes.  The default strategy is slow and safe.
+
+-o::
+	Use this for compatibility with old-style branch names used by
+	earlier versions of git-archimport.  Old-style branch names
+	were category--branch, whereas new-style branch names are
+	archive,category--branch--version.
+
+-D <depth>::
+	Follow merge ancestry and attempt to import trees that have been
+	merged from.  Specify a depth greater than 1 if patch logs have been
+	pruned.
+
+-a::
+	Attempt to auto-register archives at http://mirrors.sourcecontrol.net
+	This is particularly useful with the -D option.
+
 -t <tmpdir>::
 	Override the default tempdir.
 
diff --git a/git-archimport.perl b/git-archimport.perl
index aab4e38440e..841738d5c75 100755
--- a/git-archimport.perl
+++ b/git-archimport.perl
@@ -9,7 +9,8 @@
 
 =head1 Invocation
 
-    git-archimport [ -h ] [ -v ] [ -T ] [ -t tempdir ] <archive>/<branch> [ <archive>/<branch> ]
+    git-archimport [ -h ] [ -v ] [ -o ] [ -a ] [ -f ] [ -T ] 
+    	[ -D depth] [ -t tempdir ] <archive>/<branch> [ <archive>/<branch> ]
 
 Imports a project from one or more Arch repositories. It will follow branches
 and repositories within the namespaces defined by the <archive/branch>
@@ -74,7 +75,7 @@ our($opt_h,$opt_f,$opt_v,$opt_T,$opt_t,$opt_D,$opt_a,$opt_o);
 sub usage() {
     print STDERR <<END;
 Usage: ${\basename $0}     # fetch/update GIT from Arch
-       [ -f ] [ -o ] [ -h ] [ -v ] [ -T ] [ -a ] [ -D depth  ] [ -t tempdir ]
+       [ -h ] [ -v ] [ -o ] [ -a ] [ -f ] [ -T ] [ -D depth ] [ -t tempdir ]
        repository/arch-branch [ repository/arch-branch] ...
 END
     exit(1);

From ef1cc2cc216b37c33f1f5d68e770404c435fe1f3 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 16:16:49 -0800
Subject: [PATCH 29/43] rev-list --objects: fix object list without commit.

Earlier, "rev-list --objects <sha1>" for an object chain that
does not have any commit failed with a usage message.  This
fixes "send-pack remote $tag" where tag points at a non-commit
(e.g. a blob).

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 rev-list.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/rev-list.c b/rev-list.c
index 8020d974f2f..d0609666a1c 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -850,7 +850,8 @@ int main(int argc, const char **argv)
 		handle_one_commit(commit, &list);
 	}
 
-	if (!list)
+	if (!list &&
+	    (!(tag_objects||tree_objects||blob_objects) && !pending_objects))
 		usage(rev_list_usage);
 
 	paths = get_pathspec(prefix, argv + i);

From 3aadad1b32db3555bc19dac0892d41628a8e736d Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 16:35:48 -0800
Subject: [PATCH 30/43] Documentation: stdout of update-hook is connected to
 /dev/null

Mention that update-hook does not emit its stdout to the sender.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/hooks.txt                          | 8 ++++++++
 Documentation/howto/rebuild-from-update-hook.txt | 8 ++++++--
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt
index 7ee3571bc09..4ad1920ec10 100644
--- a/Documentation/hooks.txt
+++ b/Documentation/hooks.txt
@@ -111,6 +111,10 @@ Another use suggested on the mailing list is to use this hook to
 implement access control which is finer grained than the one
 based on filesystem group.
 
+The standard output of this hook is sent to /dev/null; if you
+want to report something to the git-send-pack on the other end,
+you can redirect your output to your stderr.
+
 post-update
 -----------
 
@@ -125,3 +129,7 @@ the outcome of `git-receive-pack`.
 The default post-update hook, when enabled, runs
 `git-update-server-info` to keep the information used by dumb
 transport up-to-date.
+
+The standard output of this hook is sent to /dev/null; if you
+want to report something to the git-send-pack on the other end,
+you can redirect your output to your stderr.
diff --git a/Documentation/howto/rebuild-from-update-hook.txt b/Documentation/howto/rebuild-from-update-hook.txt
index ebd025db85d..02621b54a03 100644
--- a/Documentation/howto/rebuild-from-update-hook.txt
+++ b/Documentation/howto/rebuild-from-update-hook.txt
@@ -10,7 +10,7 @@ The pages under http://www.kernel.org/pub/software/scm/git/docs/
 are built from Documentation/ directory of the git.git project
 and needed to be kept up-to-date.  The www.kernel.org/ servers
 are mirrored and I was told that the origin of the mirror is on
-the machine master.kernel.org, on which I was given an account
+the machine $some.kernel.org, on which I was given an account
 when I took over git maintainership from Linus.
 
 The directories relevant to this how-to are these two:
@@ -63,7 +63,7 @@ like this:
     EOF
     $ chmod +x /pub/scm/git/git.git/hooks/post-update
 
-There are three things worth mentioning:
+There are four things worth mentioning:
 
  - The update-hook is run after the repository accepts a "git
    push", under my user privilege.  It is given the full names
@@ -77,6 +77,10 @@ There are three things worth mentioning:
    pull" it does into $HOME/doc-git/docgen/ repository would not
    work correctly.
 
+ - The stdout of update hook script is not connected to git
+   push; I run the heavy part of the command inside "at", to
+   receive the execution report via e-mail.
+
  - This is still crude and does not protect against simultaneous
    make invocations stomping on each other.  I would need to add
    some locking mechanism for this.

From d89056c258bcf1164287c6c6d58ca53344bda0df Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 17:59:58 -0800
Subject: [PATCH 31/43] Remove generated files */*.py[co]

We missed ones in the compat/ subdirectory.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 9fd2ed3d92e..c66220c1a67 100644
--- a/Makefile
+++ b/Makefile
@@ -498,7 +498,7 @@ clean:
 	rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o $(LIB_FILE)
 	rm -f $(PROGRAMS) $(SIMPLE_PROGRAMS) git$X
 	rm -f $(filter-out gitk,$(SCRIPTS))
-	rm -f *.spec *.pyc *.pyo
+	rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo
 	rm -rf $(GIT_TARNAME)
 	rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
 	rm -f git-core_$(GIT_VERSION)-*.dsc
@@ -506,3 +506,4 @@ clean:
 	$(MAKE) -C Documentation/ clean
 	$(MAKE) -C templates clean
 	$(MAKE) -C t/ clean
+

From ba922ccee7565c949b4db318e5c27997cbdbfdba Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 18:02:20 -0800
Subject: [PATCH 32/43] Remove unused cmd-rename.sh

This file is a remnant from the big command rename which happened
quite some time ago.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 cmd-rename.sh | 62 ---------------------------------------------------
 1 file changed, 62 deletions(-)
 delete mode 100755 cmd-rename.sh

diff --git a/cmd-rename.sh b/cmd-rename.sh
deleted file mode 100755
index 992493de221..00000000000
--- a/cmd-rename.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/sh
-#
-# If you installed git by hand previously, you may find this
-# script useful to remove the symbolic links that we shipped
-# for backward compatibility.
-#
-# Running this script with the previous installation directory
-# like this:
-#
-# $ cmd-rename.sh /usr/local/bin/
-#
-# would clean them.
-
-d="$1"
-test -d "$d" || exit
-while read old new
-do
-	rm -f "$d/$old"
-done <<\EOF
-git-add-script	git-add
-git-archimport-script	git-archimport
-git-bisect-script	git-bisect
-git-branch-script	git-branch
-git-checkout-script	git-checkout
-git-cherry-pick-script	git-cherry-pick
-git-clone-script	git-clone
-git-commit-script	git-commit
-git-count-objects-script	git-count-objects
-git-cvsimport-script	git-cvsimport
-git-diff-script	git-diff
-git-send-email-script	git-send-email
-git-fetch-script	git-fetch
-git-format-patch-script	git-format-patch
-git-log-script	git-log
-git-ls-remote-script	git-ls-remote
-git-merge-one-file-script	git-merge-one-file
-git-octopus-script	git-octopus
-git-parse-remote-script	git-parse-remote
-git-prune-script	git-prune
-git-pull-script	git-pull
-git-push-script	git-push
-git-rebase-script	git-rebase
-git-relink-script	git-relink
-git-rename-script	git-rename
-git-repack-script	git-repack
-git-request-pull-script	git-request-pull
-git-reset-script	git-reset
-git-resolve-script	git-resolve
-git-revert-script	git-revert
-git-sh-setup-script	git-sh-setup
-git-status-script	git-status
-git-tag-script	git-tag
-git-verify-tag-script	git-verify-tag
-git-http-pull	git-http-fetch
-git-local-pull	git-local-fetch
-git-checkout-cache	git-checkout-index
-git-diff-cache	git-diff-index
-git-merge-cache	git-merge-index
-git-update-cache	git-update-index
-git-convert-cache	git-convert-objects
-git-fsck-cache	git-fsck-objects
-EOF

From e32faa8adbc8ba9803088e849bc26680134c853c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 18:03:31 -0800
Subject: [PATCH 33/43] Remove "octopus".

We still advertise "git resolve" as a standalone command, but never
"git octopus", so nobody should be using it and it is safe to
retire it.  The functionality is still available as a strategy
backend.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 .gitignore                    |  1 -
 Documentation/git-octopus.txt | 38 ---------------
 Documentation/git.txt         |  3 --
 Makefile                      |  2 +-
 git-octopus.sh                | 90 -----------------------------------
 5 files changed, 1 insertion(+), 133 deletions(-)
 delete mode 100644 Documentation/git-octopus.txt
 delete mode 100755 git-octopus.sh

diff --git a/.gitignore b/.gitignore
index 8a6bd02d4ff..6bd508e4be2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,7 +60,6 @@ git-merge-stupid
 git-mktag
 git-name-rev
 git-mv
-git-octopus
 git-pack-redundant
 git-pack-objects
 git-parse-remote
diff --git a/Documentation/git-octopus.txt b/Documentation/git-octopus.txt
deleted file mode 100644
index 6e32ea347c4..00000000000
--- a/Documentation/git-octopus.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-git-octopus(1)
-==============
-
-NAME
-----
-git-octopus - Merge more than two commits.
-
-
-SYNOPSIS
---------
-'git-octopus'
-
-DESCRIPTION
------------
-After running 'git fetch', $GIT_DIR/FETCH_HEAD contains the
-following information, one line per remote ref:
-
-------------------------------------------------
-<object name>	<ref name> from <repository>
-------------------------------------------------
-
-Using this information, create and commit an Octopus merge on
-top of the current HEAD.
-
-
-Author
-------
-Written by Junio C Hamano <junkio@cox.net>
-
-
-Documentation
---------------
-Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
-
-GIT
----
-Part of the gitlink:git[7] suite
-
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 482eba7eba2..5f068c2a1ab 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -297,9 +297,6 @@ gitlink:git-merge[1]::
 gitlink:git-mv[1]::
 	Move or rename a file, a directory, or a symlink.
 
-gitlink:git-octopus[1]::
-	Merge more than two commits.
-
 gitlink:git-pull[1]::
 	Fetch from and merge with a remote repository.
 
diff --git a/Makefile b/Makefile
index c66220c1a67..50392ff6512 100644
--- a/Makefile
+++ b/Makefile
@@ -89,7 +89,7 @@ SCRIPT_SH = \
 	git-cherry.sh git-clone.sh git-commit.sh \
 	git-count-objects.sh git-diff.sh git-fetch.sh \
 	git-format-patch.sh git-log.sh git-ls-remote.sh \
-	git-merge-one-file.sh git-octopus.sh git-parse-remote.sh \
+	git-merge-one-file.sh git-parse-remote.sh \
 	git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
 	git-repack.sh git-request-pull.sh git-reset.sh \
 	git-resolve.sh git-revert.sh git-sh-setup.sh git-status.sh \
diff --git a/git-octopus.sh b/git-octopus.sh
deleted file mode 100755
index 2edbf52c42d..00000000000
--- a/git-octopus.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-# Resolve two or more trees recorded in $GIT_DIR/FETCH_HEAD.
-#
-. git-sh-setup
-
-usage () {
-    die "usage: git octopus"
-}
-
-# Sanity check the heads early.
-while read SHA1 REPO
-do
-	test $(git-cat-file -t $SHA1) = "commit" ||
-		die "$REPO given to octopus is not a commit"
-done <"$GIT_DIR/FETCH_HEAD"
-
-head=$(git-rev-parse --verify HEAD) || exit
-
-git-update-index --refresh ||
-	die "Your working tree is dirty."
-test "$(git-diff-index --cached "$head")" = "" ||
-	die "Your working tree does not match HEAD."
-
-# MRC is the current "merge reference commit"
-# MRT is the current "merge result tree"
-
-MRC=$head PARENT="-p $head"
-MRT=$(git-write-tree)
-CNT=1 ;# counting our head
-NON_FF_MERGE=0
-while read SHA1 REPO
-do
-	common=$(git-merge-base $MRC $SHA1) ||
-		die "Unable to find common commit with $SHA1 from $REPO"
-
-	if test "$common" = $SHA1
-	then
-		echo "Already up-to-date: $REPO"
-		continue
-	fi
-
-	CNT=`expr $CNT + 1`
-	PARENT="$PARENT -p $SHA1"
-
-	if test "$common,$NON_FF_MERGE" = "$MRC,0"
-	then
-		# The first head being merged was a fast-forward.
-		# Advance MRC to the head being merged, and use that
-		# tree as the intermediate result of the merge.
-		# We still need to count this as part of the parent set.
-
-		echo "Fast forwarding to: $REPO"
-		git-read-tree -u -m $head $SHA1 || exit
-		MRC=$SHA1 MRT=$(git-write-tree)
-		continue
-	fi
-
-	NON_FF_MERGE=1
-
-	echo "Trying simple merge with $REPO"
-	git-read-tree -u -m $common $MRT $SHA1 || exit
-	next=$(git-write-tree 2>/dev/null)
-	if test $? -ne 0
-	then
-		echo "Simple merge did not work, trying automatic merge."
-		git-merge-index -o git-merge-one-file -a || {
-		git-read-tree --reset "$head"
-		git-checkout-index -f -q -u -a
-		die "Automatic merge failed; should not be doing Octopus"
-		}
-		next=$(git-write-tree 2>/dev/null)
-	fi
-	MRC=$common
-	MRT=$next
-done <"$GIT_DIR/FETCH_HEAD"
-
-# Just to be careful in case the user feeds nonsense to us.
-case "$CNT" in
-1)
-	echo "No changes."
-	exit 0 ;;
-esac
-result_commit=$(git-fmt-merge-msg <"$GIT_DIR/FETCH_HEAD" |
-		git-commit-tree $MRT $PARENT)
-echo "Committed merge $result_commit"
-git-update-ref HEAD $result_commit $head
-git-diff-tree -p $head $result_commit | git-apply --stat

From 1fdfd05db2f6e6bacd8c8255992fa4a7f1756176 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 18:27:04 -0800
Subject: [PATCH 34/43] tests: make scripts executable

just for consistency.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t1200-tutorial.sh          | 0
 t/t1300-repo-config.sh       | 0
 t/t3101-ls-tree-dirname.sh   | 0
 t/t4103-apply-binary.sh      | 0
 t/t4109-apply-multifrag.sh   | 0
 t/t4110-apply-scan.sh        | 0
 t/t5500-fetch-pack.sh        | 0
 t/t6101-rev-parse-parents.sh | 0
 8 files changed, 0 insertions(+), 0 deletions(-)
 mode change 100644 => 100755 t/t1200-tutorial.sh
 mode change 100644 => 100755 t/t1300-repo-config.sh
 mode change 100644 => 100755 t/t3101-ls-tree-dirname.sh
 mode change 100644 => 100755 t/t4103-apply-binary.sh
 mode change 100644 => 100755 t/t4109-apply-multifrag.sh
 mode change 100644 => 100755 t/t4110-apply-scan.sh
 mode change 100644 => 100755 t/t5500-fetch-pack.sh
 mode change 100644 => 100755 t/t6101-rev-parse-parents.sh

diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
old mode 100644
new mode 100755
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
old mode 100644
new mode 100755
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
old mode 100644
new mode 100755
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
old mode 100644
new mode 100755
diff --git a/t/t4109-apply-multifrag.sh b/t/t4109-apply-multifrag.sh
old mode 100644
new mode 100755
diff --git a/t/t4110-apply-scan.sh b/t/t4110-apply-scan.sh
old mode 100644
new mode 100755
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
old mode 100644
new mode 100755
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
old mode 100644
new mode 100755

From 1c15afb9343bca82e687d008ec983a9110ac9c40 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 16:18:28 -0800
Subject: [PATCH 35/43] xread/xwrite: do not worry about EINTR at calling
 sites.

We had errno==EINTR check after read(2)/write(2) sprinkled all
over the places, always doing continue.  Consolidate them into
xread()/xwrite() wrapper routines.

Credits for suggestion goes to HPA -- bugs are mine.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 apply.c           | 23 ++++++-----------------
 cat-file.c        |  4 +---
 copy.c            | 19 +++++++------------
 csum-file.c       |  4 +---
 git-compat-util.h | 22 ++++++++++++++++++++++
 mktag.c           |  9 ++-------
 pkt-line.c        | 11 +++--------
 tar-tree.c        |  4 +---
 unpack-objects.c  | 13 +++----------
 9 files changed, 46 insertions(+), 63 deletions(-)

diff --git a/apply.c b/apply.c
index 1742ab28e95..d5e7bfdb4de 100644
--- a/apply.c
+++ b/apply.c
@@ -84,14 +84,11 @@ static void *read_patch_file(int fd, unsigned long *sizep)
 			buffer = xrealloc(buffer, alloc);
 			nr = alloc - size;
 		}
-		nr = read(fd, buffer + size, nr);
+		nr = xread(fd, buffer + size, nr);
 		if (!nr)
 			break;
-		if (nr < 0) {
-			if (errno == EAGAIN)
-				continue;
+		if (nr < 0)
 			die("git-apply: read returned %s", strerror(errno));
-		}
 		size += nr;
 	}
 	*sizep = size;
@@ -1006,13 +1003,8 @@ static int read_old_data(struct stat *st, const char *path, void *buf, unsigned
 			return error("unable to open %s", path);
 		got = 0;
 		for (;;) {
-			int ret = read(fd, buf + got, size - got);
-			if (ret < 0) {
-				if (errno == EAGAIN)
-					continue;
-				break;
-			}
-			if (!ret)
+			int ret = xread(fd, buf + got, size - got);
+			if (ret <= 0)
 				break;
 			got += ret;
 		}
@@ -1600,12 +1592,9 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
 	if (fd < 0)
 		return -1;
 	while (size) {
-		int written = write(fd, buf, size);
-		if (written < 0) {
-			if (errno == EINTR || errno == EAGAIN)
-				continue;
+		int written = xwrite(fd, buf, size);
+		if (written < 0)
 			die("writing file %s: %s", path, strerror(errno));
-		}
 		if (!written)
 			die("out of space writing file %s", path);
 		buf += written;
diff --git a/cat-file.c b/cat-file.c
index 7594108c6e5..96d66b43043 100644
--- a/cat-file.c
+++ b/cat-file.c
@@ -55,10 +55,8 @@ int main(int argc, char **argv)
 		die("git-cat-file %s: bad file", argv[2]);
 
 	while (size > 0) {
-		long ret = write(1, buf, size);
+		long ret = xwrite(1, buf, size);
 		if (ret < 0) {
-			if (errno == EAGAIN)
-				continue;
 			/* Ignore epipe */
 			if (errno == EPIPE)
 				break;
diff --git a/copy.c b/copy.c
index e1cd5d06503..7100eedbc32 100644
--- a/copy.c
+++ b/copy.c
@@ -6,32 +6,27 @@ int copy_fd(int ifd, int ofd)
 		int len;
 		char buffer[8192];
 		char *buf = buffer;
-		len = read(ifd, buffer, sizeof(buffer));
+		len = xread(ifd, buffer, sizeof(buffer));
 		if (!len)
 			break;
 		if (len < 0) {
 			int read_error;
-			if (errno == EAGAIN)
-				continue;
 			read_error = errno;
 			close(ifd);
 			return error("copy-fd: read returned %s",
 				     strerror(read_error));
 		}
-		while (1) {
-			int written = write(ofd, buf, len);
+		while (len) {
+			int written = xwrite(ofd, buf, len);
 			if (written > 0) {
 				buf += written;
 				len -= written;
-				if (!len)
-					break;
 			}
-			if (!written)
+			else if (!written)
 				return error("copy-fd: write returned 0");
-			if (errno == EAGAIN || errno == EINTR)
-				continue;
-			return error("copy-fd: write returned %s",
-				     strerror(errno));
+			else
+				return error("copy-fd: write returned %s",
+					     strerror(errno));
 		}
 	}
 	close(ifd);
diff --git a/csum-file.c b/csum-file.c
index c66b9eb10bf..5f9249aeedf 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -15,7 +15,7 @@ static int sha1flush(struct sha1file *f, unsigned int count)
 	void *buf = f->buffer;
 
 	for (;;) {
-		int ret = write(f->fd, buf, count);
+		int ret = xwrite(f->fd, buf, count);
 		if (ret > 0) {
 			buf += ret;
 			count -= ret;
@@ -25,8 +25,6 @@ static int sha1flush(struct sha1file *f, unsigned int count)
 		}
 		if (!ret)
 			die("sha1 file '%s' write error. Out of diskspace", f->name);
-		if (errno == EAGAIN || errno == EINTR)
-			continue;
 		die("sha1 file '%s' write error (%s)", f->name, strerror(errno));
 	}
 }
diff --git a/git-compat-util.h b/git-compat-util.h
index ead0ede5872..0c98c9937df 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -84,6 +84,28 @@ static inline void *xcalloc(size_t nmemb, size_t size)
 	return ret;
 }
 
+static inline ssize_t xread(int fd, void *buf, size_t len)
+{
+	ssize_t nr;
+	while (1) {
+		nr = read(fd, buf, len);
+		if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
+			continue;
+		return nr;
+	}
+}
+
+static inline ssize_t xwrite(int fd, const void *buf, size_t len)
+{
+	ssize_t nr;
+	while (1) {
+		nr = write(fd, buf, len);
+		if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
+			continue;
+		return nr;
+	}
+}
+
 /* Sane ctype - no locale, and works with signed chars */
 #undef isspace
 #undef isdigit
diff --git a/mktag.c b/mktag.c
index 97e270a5761..fc6a9bf5f34 100644
--- a/mktag.c
+++ b/mktag.c
@@ -116,14 +116,9 @@ int main(int argc, char **argv)
 	// Read the signature
 	size = 0;
 	for (;;) {
-		int ret = read(0, buffer + size, MAXSIZE - size);
-		if (!ret)
+		int ret = xread(0, buffer + size, MAXSIZE - size);
+		if (ret <= 0)
 			break;
-		if (ret < 0) {
-			if (errno == EAGAIN)
-				continue;
-			break;
-		}
 		size += ret;
 	}
 
diff --git a/pkt-line.c b/pkt-line.c
index 69473046bf7..bb3bab05cd2 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -19,7 +19,7 @@
 static void safe_write(int fd, const void *buf, unsigned n)
 {
 	while (n) {
-		int ret = write(fd, buf, n);
+		int ret = xwrite(fd, buf, n);
 		if (ret > 0) {
 			buf += ret;
 			n -= ret;
@@ -27,8 +27,6 @@ static void safe_write(int fd, const void *buf, unsigned n)
 		}
 		if (!ret)
 			die("write error (disk full?)");
-		if (errno == EAGAIN || errno == EINTR)
-			continue;
 		die("write error (%s)", strerror(errno));
 	}
 }
@@ -68,12 +66,9 @@ static void safe_read(int fd, void *buffer, unsigned size)
 	int n = 0;
 
 	while (n < size) {
-		int ret = read(fd, buffer + n, size - n);
-		if (ret < 0) {
-			if (errno == EINTR || errno == EAGAIN)
-				continue;
+		int ret = xread(fd, buffer + n, size - n);
+		if (ret < 0)
 			die("read error (%s)", strerror(errno));
-		}
 		if (!ret)
 			die("unexpected EOF");
 		n += ret;
diff --git a/tar-tree.c b/tar-tree.c
index bacb23ae636..96bd1438d90 100644
--- a/tar-tree.c
+++ b/tar-tree.c
@@ -34,10 +34,8 @@ struct path_prefix {
 static void reliable_write(void *buf, unsigned long size)
 {
 	while (size > 0) {
-		long ret = write(1, buf, size);
+		long ret = xwrite(1, buf, size);
 		if (ret < 0) {
-			if (errno == EAGAIN)
-				continue;
 			if (errno == EPIPE)
 				exit(0);
 			die("git-tar-tree: %s", strerror(errno));
diff --git a/unpack-objects.c b/unpack-objects.c
index cfd61ae6b08..5c5cb12f6fa 100644
--- a/unpack-objects.c
+++ b/unpack-objects.c
@@ -31,12 +31,10 @@ static void * fill(int min)
 		offset = 0;
 	}
 	do {
-		int ret = read(0, buffer + len, sizeof(buffer) - len);
+		int ret = xread(0, buffer + len, sizeof(buffer) - len);
 		if (ret <= 0) {
 			if (!ret)
 				die("early EOF");
-			if (errno == EAGAIN || errno == EINTR)
-				continue;
 			die("read error on input: %s", strerror(errno));
 		}
 		len += ret;
@@ -299,14 +297,9 @@ int main(int argc, char **argv)
 
 	/* Write the last part of the buffer to stdout */
 	while (len) {
-		int ret = write(1, buffer + offset, len);
-		if (!ret)
+		int ret = xwrite(1, buffer + offset, len);
+		if (ret <= 0)
 			break;
-		if (ret < 0) {
-			if (errno == EAGAIN || errno == EINTR)
-				continue;
-			break;
-		}
 		len -= ret;
 		offset += ret;
 	}

From 47dd0d595d04ee5283dfd8a0b4cbd6e6de8ad57f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 13 Dec 2005 17:21:41 -0800
Subject: [PATCH 36/43] diff: --abbrev option

When I show transcripts to explain how something works, I often
find myself hand-editing the diff-raw output to shorten various
object names in the output.

This adds --abbrev option to the diff family, which shortens
diff-raw output and diff-tree commit id headers.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/diff-options.txt |  7 +++++
 diff-tree.c                    | 40 ++++++++++++++----------
 diff.c                         | 57 ++++++++++++++++++++++++++++++----
 diff.h                         |  9 +++++-
 sha1_name.c                    |  3 ++
 5 files changed, 93 insertions(+), 23 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 6b496ede255..3d1175e8646 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -18,6 +18,13 @@
 	object name of pre- and post-image blob on the "index"
 	line when generating a patch format output.	
 
+--abbrev::
+	Instead of showing the full 40-byte hexadecimal object
+	name in diff-raw format output and diff-tree header
+	lines, show only handful prefix.  This is independent of
+	--full-index option above, which controls the diff-patch
+	output format.
+
 -B::
 	Break complete rewrite changes into pairs of delete and create.
 
diff --git a/diff-tree.c b/diff-tree.c
index d56d921585d..efa2b9476ea 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -14,11 +14,6 @@ static enum cmit_fmt commit_format = CMIT_FMT_RAW;
 
 static struct diff_options diff_options;
 
-static void call_diff_setup_done(void)
-{
-	diff_setup_done(&diff_options);
-}
-
 static int call_diff_flush(void)
 {
 	diffcore_std(&diff_options);
@@ -43,7 +38,6 @@ static int diff_tree_sha1_top(const unsigned char *old,
 {
 	int ret;
 
-	call_diff_setup_done();
 	ret = diff_tree_sha1(old, new, base, &diff_options);
 	call_diff_flush();
 	return ret;
@@ -55,7 +49,6 @@ static int diff_root_tree(const unsigned char *new, const char *base)
 	void *tree;
 	struct tree_desc empty, real;
 
-	call_diff_setup_done();
 	tree = read_object_with_reference(new, "tree", &real.size, NULL);
 	if (!tree)
 		die("unable to read root tree (%s)", sha1_to_hex(new));
@@ -69,18 +62,29 @@ static int diff_root_tree(const unsigned char *new, const char *base)
 	return retval;
 }
 
-static const char *generate_header(const char *commit, const char *parent, const char *msg)
+static const char *generate_header(const unsigned char *commit_sha1,
+				   const unsigned char *parent_sha1,
+				   const char *msg)
 {
 	static char this_header[16384];
 	int offset;
 	unsigned long len;
+	int abbrev = diff_options.abbrev;
 
 	if (!verbose_header)
-		return commit;
+		return sha1_to_hex(commit_sha1);
 
 	len = strlen(msg);
-	offset = sprintf(this_header, "%s%s (from %s)\n", header_prefix, commit, parent);
-	offset += pretty_print_commit(commit_format, msg, len, this_header + offset, sizeof(this_header) - offset);
+
+	offset = sprintf(this_header, "%s%s ",
+			 header_prefix,
+			 diff_unique_abbrev(commit_sha1, abbrev));
+	offset += sprintf(this_header + offset, "(from %s)\n",
+			 parent_sha1 ?
+			 diff_unique_abbrev(parent_sha1, abbrev) : "root");
+	offset += pretty_print_commit(commit_format, msg, len,
+				      this_header + offset,
+				      sizeof(this_header) - offset);
 	return this_header;
 }
 
@@ -99,18 +103,18 @@ static int diff_tree_commit(const unsigned char *commit_sha1)
 	
 	/* Root commit? */
 	if (show_root_diff && !commit->parents) {
-		header = generate_header(name, "root", commit->buffer);
+		header = generate_header(sha1, NULL, commit->buffer);
 		diff_root_tree(commit_sha1, "");
 	}
 
 	/* More than one parent? */
 	if (ignore_merges && commit->parents && commit->parents->next)
-			return 0;
+		return 0;
 
 	for (parents = commit->parents; parents; parents = parents->next) {
 		struct commit *parent = parents->item;
-		header = generate_header(name,
-					 sha1_to_hex(parent->object.sha1),
+		header = generate_header(sha1,
+					 parent->object.sha1,
 					 commit->buffer);
 		diff_tree_sha1_top(parent->object.sha1, commit_sha1, "");
 		if (!header && verbose_header) {
@@ -129,6 +133,7 @@ static int diff_tree_stdin(char *line)
 	int len = strlen(line);
 	unsigned char commit[20], parent[20];
 	static char this_header[1000];
+	int abbrev = diff_options.abbrev;
 
 	if (!len || line[len-1] != '\n')
 		return -1;
@@ -138,7 +143,9 @@ static int diff_tree_stdin(char *line)
 	if (isspace(line[40]) && !get_sha1_hex(line+41, parent)) {
 		line[40] = 0;
 		line[81] = 0;
-		sprintf(this_header, "%s (from %s)\n", line, line+41);
+		sprintf(this_header, "%s (from %s)\n",
+			diff_unique_abbrev(commit, abbrev),
+			diff_unique_abbrev(parent, abbrev));
 		header = this_header;
 		return diff_tree_sha1_top(parent, commit, "");
 	}
@@ -239,6 +246,7 @@ int main(int argc, const char **argv)
 		diff_options.recursive = 1;
 
 	diff_tree_setup_paths(get_pathspec(prefix, argv));
+	diff_setup_done(&diff_options);
 
 	switch (nr_sha1) {
 	case 0:
diff --git a/diff.c b/diff.c
index 2e0797bf3ee..c8159183dac 100644
--- a/diff.c
+++ b/diff.c
@@ -723,11 +723,13 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 
 	if (memcmp(one->sha1, two->sha1, 20)) {
 		char one_sha1[41];
-		const char *index_fmt = o->full_index ? "index %s..%s" : "index %.7s..%.7s";
+		int abbrev = o->full_index ? 40 : DIFF_DEFAULT_INDEX_ABBREV;
 		memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
 
 		len += snprintf(msg + len, sizeof(msg) - len,
-				index_fmt, one_sha1, sha1_to_hex(two->sha1));
+				"index %.*s..%.*s",
+				abbrev, one_sha1, abbrev,
+				sha1_to_hex(two->sha1));
 		if (one->mode == two->mode)
 			len += snprintf(msg + len, sizeof(msg) - len,
 					" %06o", one->mode);
@@ -791,6 +793,8 @@ int diff_setup_done(struct diff_options *options)
 	}
 	if (options->setup & DIFF_SETUP_USE_SIZE_CACHE)
 		use_size_cache = 1;
+	if (options->abbrev <= 0 || 40 < options->abbrev)
+		options->abbrev = 40; /* full */
 
 	return 0;
 }
@@ -841,6 +845,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 	}
 	else if (!strcmp(arg, "--find-copies-harder"))
 		options->find_copies_harder = 1;
+	else if (!strcmp(arg, "--abbrev"))
+		options->abbrev = DIFF_DEFAULT_ABBREV;
+	else if (!strncmp(arg, "--abbrev=", 9))
+		options->abbrev = strtoul(arg + 9, NULL, 10);
 	else
 		return 0;
 	return 1;
@@ -947,14 +955,49 @@ void diff_free_filepair(struct diff_filepair *p)
 	free(p);
 }
 
+/* This is different from find_unique_abbrev() in that
+ * it needs to deal with 0{40} SHA1.
+ */
+const char *diff_unique_abbrev(const unsigned char *sha1, int len)
+{
+	int abblen;
+	const char *abbrev;
+	if (len == 40)
+		return sha1_to_hex(sha1);
+
+	abbrev = find_unique_abbrev(sha1, len);
+	if (!abbrev) {
+		if (!memcmp(sha1, null_sha1, 20)) {
+			char *buf = sha1_to_hex(null_sha1);
+			if (len < 37)
+				strcpy(buf + len, "...");
+			return buf;
+		}
+		else 
+			return sha1_to_hex(sha1);
+	}
+	abblen = strlen(abbrev);
+	if (abblen < 37) {
+		static char hex[41];
+		if (len < abblen && abblen <= len + 2)
+			sprintf(hex, "%s%.*s", abbrev, len+3-abblen, "..");
+		else
+			sprintf(hex, "%s...", abbrev);
+		return hex;
+	}
+	return sha1_to_hex(sha1);
+}
+
 static void diff_flush_raw(struct diff_filepair *p,
 			   int line_termination,
 			   int inter_name_termination,
-			   int output_format)
+			   struct diff_options *options)
 {
 	int two_paths;
 	char status[10];
+	int abbrev = options->abbrev;
 	const char *path_one, *path_two;
+	int output_format = options->output_format;
 
 	path_one = p->one->path;
 	path_two = p->two->path;
@@ -985,8 +1028,10 @@ static void diff_flush_raw(struct diff_filepair *p,
 	}
 	if (output_format != DIFF_FORMAT_NAME_STATUS) {
 		printf(":%06o %06o %s ",
-		       p->one->mode, p->two->mode, sha1_to_hex(p->one->sha1));
-		printf("%s ", sha1_to_hex(p->two->sha1));
+		       p->one->mode, p->two->mode,
+		       diff_unique_abbrev(p->one->sha1, abbrev));
+		printf("%s ",
+		       diff_unique_abbrev(p->two->sha1, abbrev));
 	}
 	printf("%s%c%s", status, inter_name_termination, path_one);
 	if (two_paths)
@@ -1194,7 +1239,7 @@ void diff_flush(struct diff_options *options)
 		case DIFF_FORMAT_NAME_STATUS:
 			diff_flush_raw(p, line_termination,
 				       inter_name_termination,
-				       diff_output_format);
+				       options);
 			break;
 		case DIFF_FORMAT_NAME:
 			diff_flush_name(p,
diff --git a/diff.h b/diff.h
index 32b4780173d..c3486ffa861 100644
--- a/diff.h
+++ b/diff.h
@@ -44,6 +44,7 @@ struct diff_options {
 	int reverse_diff;
 	int rename_limit;
 	int setup;
+	int abbrev;
 
 	change_fn_t change;
 	add_remove_fn_t add_remove;
@@ -87,6 +88,9 @@ extern int diff_setup_done(struct diff_options *);
 
 #define DIFF_PICKAXE_ALL	1
 
+#define DIFF_DEFAULT_INDEX_ABBREV	7 /* hex digits */
+#define DIFF_DEFAULT_ABBREV	7 /* hex digits */
+
 extern void diffcore_std(struct diff_options *);
 
 extern void diffcore_std_no_resolve(struct diff_options *);
@@ -98,7 +102,8 @@ extern void diffcore_std_no_resolve(struct diff_options *);
 "  -u            synonym for -p.\n" \
 "  --name-only   show only names of changed files.\n" \
 "  --name-status show names and status of changed files.\n" \
-"  --full-index  show full object name on index ines.\n" \
+"  --full-index  show full object name on index lines.\n" \
+"  --abbrev      abbreviate object names in diff-tree header and diff-raw.\n" \
 "  -R            swap input file pairs.\n" \
 "  -B            detect complete rewrites.\n" \
 "  -M            detect renames.\n" \
@@ -137,4 +142,6 @@ extern void diff_flush(struct diff_options*);
 #define DIFF_STATUS_FILTER_AON		'*'
 #define DIFF_STATUS_FILTER_BROKEN	'B'
 
+extern const char *diff_unique_abbrev(const unsigned char *, int);
+
 #endif /* DIFF_H */
diff --git a/sha1_name.c b/sha1_name.c
index 67b69a54fb7..b13ed78cee0 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -188,7 +188,10 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
 	int status;
 	static char hex[41];
+
 	memcpy(hex, sha1_to_hex(sha1), 40);
+	if (len == 40)
+		return hex;
 	while (len < 40) {
 		unsigned char sha1_ret[20];
 		status = get_short_sha1(hex, len, sha1_ret, 1);

From 913419fcc648dd43d7f7afdd94fa25aee4f9798a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 18 Dec 2005 02:03:15 -0800
Subject: [PATCH 37/43] diff --abbrev: document --abbrev=<n> form.

It was implemented there but was not advertised.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/diff-options.txt | 9 +++++----
 diff.h                         | 2 +-
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 3d1175e8646..9e574a04d39 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -18,12 +18,13 @@
 	object name of pre- and post-image blob on the "index"
 	line when generating a patch format output.	
 
---abbrev::
+--abbrev[=<n>]::
 	Instead of showing the full 40-byte hexadecimal object
 	name in diff-raw format output and diff-tree header
-	lines, show only handful prefix.  This is independent of
-	--full-index option above, which controls the diff-patch
-	output format.
+	lines, show only handful dhexigits prefix.  This is
+	independent of --full-index option above, which controls
+	the diff-patch output format.  Non default number of
+	digits can be specified with --abbrev=<n>.
 
 -B::
 	Break complete rewrite changes into pairs of delete and create.
diff --git a/diff.h b/diff.h
index c3486ffa861..5696f2aff06 100644
--- a/diff.h
+++ b/diff.h
@@ -103,7 +103,7 @@ extern void diffcore_std_no_resolve(struct diff_options *);
 "  --name-only   show only names of changed files.\n" \
 "  --name-status show names and status of changed files.\n" \
 "  --full-index  show full object name on index lines.\n" \
-"  --abbrev      abbreviate object names in diff-tree header and diff-raw.\n" \
+"  --abbrev=<n>  abbreviate object names in diff-tree header and diff-raw.\n" \
 "  -R            swap input file pairs.\n" \
 "  -B            detect complete rewrites.\n" \
 "  -M            detect renames.\n" \

From a5c21d6eb7e7ce164666d55e5911c271664ddfe7 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 19 Dec 2005 21:55:12 -0800
Subject: [PATCH 38/43] format-patch: make sure header and body are separated.

Since log message in a commit object is defined to be binary
blob, it could be something without an empty line between the
title line and the body text.  Be careful to format such into
a form suitable for e-mail submission.  There must be an empty
line between the headers and the body.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-format-patch.sh | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/git-format-patch.sh b/git-format-patch.sh
index 01508e3b04f..daa3caea778 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -210,6 +210,8 @@ Date: '"$ad"
 	}
 
 	mailScript="$mailScript"'
+	a\
+
 	: body
 	p
 	n

From 29e4d3635709778bcc808dbad0477efad82f8d7e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 20 Dec 2005 00:02:15 -0800
Subject: [PATCH 39/43] Racy GIT

This fixes the longstanding "Racy GIT" problem, which was pretty
much there from the beginning of time, but was first
demonstrated by Pasky in this message on October 24, 2005:

    http://marc.theaimsgroup.com/?l=git&m=113014629716878

If you run the following sequence of commands:

	echo frotz >infocom
        git update-index --add infocom
        echo xyzzy >infocom

so that the second update to file "infocom" does not change
st_mtime, what is recorded as the stat information for the cache
entry "infocom" exactly matches what is on the filesystem
(owner, group, inum, mtime, ctime, mode, length).  After this
sequence, we incorrectly think "infocom" file still has string
"frotz" in it, and get really confused.  E.g. git-diff-files
would say there is no change, git-update-index --refresh would
not even look at the filesystem to correct the situation.

Some ways of working around this issue were already suggested by
Linus in the same thread on the same day, including waiting
until the next second before returning from update-index if a
cache entry written out has the current timestamp, but that
means we can make at most one commit per second, and given that
the e-mail patch workflow used by Linus needs to process at
least 5 commits per second, it is not an acceptable solution.
Linus notes that git-apply is primarily used to update the index
while processing e-mailed patches, which is true, and
git-apply's up-to-date check is fooled by the same problem but
luckily in the other direction, so it is not really a big issue,
but still it is disturbing.

The function ce_match_stat() is called to bypass the comparison
against filesystem data when the stat data recorded in the cache
entry matches what stat() returns from the filesystem.  This
patch tackles the problem by changing it to actually go to the
filesystem data for cache entries that have the same mtime as
the index file itself.  This works as long as the index file and
working tree files are on the filesystems that share the same
monotonic clock.  Files on network mounted filesystems sometimes
get skewed timestamps compared to "date" output, but as long as
working tree files' timestamps are skewed the same way as the
index file's, this approach still works.  The only problematic
files are the ones that have the same timestamp as the index
file's, because two file updates that sandwitch the index file
update must happen within the same second to trigger the
problem.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 read-cache.c        | 142 +++++++++++++++++++++++++++-----------------
 t/t0010-racy-git.sh |  24 ++++++++
 2 files changed, 111 insertions(+), 55 deletions(-)
 create mode 100755 t/t0010-racy-git.sh

diff --git a/read-cache.c b/read-cache.c
index 69327362037..780601f071a 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -6,6 +6,7 @@
 #include "cache.h"
 
 struct cache_entry **active_cache = NULL;
+static time_t index_file_timestamp;
 unsigned int active_nr = 0, active_alloc = 0, active_cache_changed = 0;
 
 /*
@@ -28,6 +29,64 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
 	ce->ce_size = htonl(st->st_size);
 }
 
+static int ce_compare_data(struct cache_entry *ce, struct stat *st)
+{
+	int match = -1;
+	int fd = open(ce->name, O_RDONLY);
+
+	if (fd >= 0) {
+		unsigned char sha1[20];
+		if (!index_fd(sha1, fd, st, 0, NULL))
+			match = memcmp(sha1, ce->sha1, 20);
+		close(fd);
+	}
+	return match;
+}
+
+static int ce_compare_link(struct cache_entry *ce, unsigned long expected_size)
+{
+	int match = -1;
+	char *target;
+	void *buffer;
+	unsigned long size;
+	char type[10];
+	int len;
+
+	target = xmalloc(expected_size);
+	len = readlink(ce->name, target, expected_size);
+	if (len != expected_size) {
+		free(target);
+		return -1;
+	}
+	buffer = read_sha1_file(ce->sha1, type, &size);
+	if (!buffer) {
+		free(target);
+		return -1;
+	}
+	if (size == expected_size)
+		match = memcmp(buffer, target, size);
+	free(buffer);
+	free(target);
+	return match;
+}
+
+static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
+{
+	switch (st->st_mode & S_IFMT) {
+	case S_IFREG:
+		if (ce_compare_data(ce, st))
+			return DATA_CHANGED;
+		break;
+	case S_IFLNK:
+		if (ce_compare_link(ce, st->st_size))
+			return DATA_CHANGED;
+		break;
+	default:
+		return TYPE_CHANGED;
+	}
+	return 0;
+}
+
 int ce_match_stat(struct cache_entry *ce, struct stat *st)
 {
 	unsigned int changed = 0;
@@ -83,57 +142,37 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
 
 	if (ce->ce_size != htonl(st->st_size))
 		changed |= DATA_CHANGED;
+
+	/*
+	 * Within 1 second of this sequence:
+	 * 	echo xyzzy >file && git-update-index --add file
+	 * running this command:
+	 * 	echo frotz >file
+	 * would give a falsely clean cache entry.  The mtime and
+	 * length match the cache, and other stat fields do not change.
+	 *
+	 * We could detect this at update-index time (the cache entry
+	 * being registered/updated records the same time as "now")
+	 * and delay the return from git-update-index, but that would
+	 * effectively mean we can make at most one commit per second,
+	 * which is not acceptable.  Instead, we check cache entries
+	 * whose mtime are the same as the index file timestamp more
+	 * careful than others.
+	 */
+	if (!changed &&
+	    index_file_timestamp &&
+	    index_file_timestamp <= ntohl(ce->ce_mtime.sec))
+		changed |= ce_modified_check_fs(ce, st);
+
 	return changed;
 }
 
-static int ce_compare_data(struct cache_entry *ce, struct stat *st)
-{
-	int match = -1;
-	int fd = open(ce->name, O_RDONLY);
-
-	if (fd >= 0) {
-		unsigned char sha1[20];
-		if (!index_fd(sha1, fd, st, 0, NULL))
-			match = memcmp(sha1, ce->sha1, 20);
-		close(fd);
-	}
-	return match;
-}
-
-static int ce_compare_link(struct cache_entry *ce, unsigned long expected_size)
-{
-	int match = -1;
-	char *target;
-	void *buffer;
-	unsigned long size;
-	char type[10];
-	int len;
-
-	target = xmalloc(expected_size);
-	len = readlink(ce->name, target, expected_size);
-	if (len != expected_size) {
-		free(target);
-		return -1;
-	}
-	buffer = read_sha1_file(ce->sha1, type, &size);
-	if (!buffer) {
-		free(target);
-		return -1;
-	}
-	if (size == expected_size)
-		match = memcmp(buffer, target, size);
-	free(buffer);
-	free(target);
-	return match;
-}
-
 int ce_modified(struct cache_entry *ce, struct stat *st)
 {
-	int changed;
+	int changed, changed_fs;
 	changed = ce_match_stat(ce, st);
 	if (!changed)
 		return 0;
-
 	/*
 	 * If the mode or type has changed, there's no point in trying
 	 * to refresh the entry - it's not going to match
@@ -148,18 +187,9 @@ int ce_modified(struct cache_entry *ce, struct stat *st)
 	if ((changed & DATA_CHANGED) && ce->ce_size != htonl(0))
 		return changed;
 
-	switch (st->st_mode & S_IFMT) {
-	case S_IFREG:
-		if (ce_compare_data(ce, st))
-			return changed | DATA_CHANGED;
-		break;
-	case S_IFLNK:
-		if (ce_compare_link(ce, st->st_size))
-			return changed | DATA_CHANGED;
-		break;
-	default:
-		return changed | TYPE_CHANGED;
-	}
+	changed_fs = ce_modified_check_fs(ce, st);
+	if (changed_fs)
+		return changed | changed_fs;
 	return 0;
 }
 
@@ -471,6 +501,7 @@ int read_cache(void)
 		return active_nr;
 
 	errno = ENOENT;
+	index_file_timestamp = 0;
 	fd = open(get_index_file(), O_RDONLY);
 	if (fd < 0) {
 		if (errno == ENOENT)
@@ -504,6 +535,7 @@ int read_cache(void)
 		offset = offset + ce_size(ce);
 		active_cache[i] = ce;
 	}
+	index_file_timestamp = st.st_mtime;
 	return active_nr;
 
 unmap:
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
new file mode 100755
index 00000000000..eb175b780f4
--- /dev/null
+++ b/t/t0010-racy-git.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='racy GIT'
+
+. ./test-lib.sh
+
+# This test can give false success if your machine is sufficiently
+# slow or your trial happened to happen on second boundary.
+
+for trial in 0 1 2 3 4 5 6 7 8 9
+do
+	rm -f .git/index
+	echo frotz >infocom
+	echo xyzzy >activision
+	git update-index --add infocom activision
+	echo xyzzy >infocom
+
+	files=`git diff-files -p`
+	test_expect_success \
+	"Racy GIT trial #$trial" \
+	'test "" != "$files"'
+done
+
+test_done

From 407c8eb0d09d4b84bbfda9e04895a35c8fd6fef6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 20 Dec 2005 12:12:18 -0800
Subject: [PATCH 40/43] Racy GIT (part #2)

The previous round caught the most trivial case well, but broke
down once index file is updated again.  Smudge problematic
entries (they should be very few if any under normal interactive
workflow) before writing a new index file out.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 read-cache.c        | 32 +++++++++++++++++++++++++++++++-
 t/t0010-racy-git.sh | 17 +++++++++++++----
 2 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/read-cache.c b/read-cache.c
index 780601f071a..afdc2b075b0 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -87,7 +87,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
 	return 0;
 }
 
-int ce_match_stat(struct cache_entry *ce, struct stat *st)
+static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 {
 	unsigned int changed = 0;
 
@@ -143,6 +143,13 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st)
 	if (ce->ce_size != htonl(st->st_size))
 		changed |= DATA_CHANGED;
 
+	return changed;
+}
+
+int ce_match_stat(struct cache_entry *ce, struct stat *st)
+{
+	unsigned int changed = ce_match_stat_basic(ce, st);
+
 	/*
 	 * Within 1 second of this sequence:
 	 * 	echo xyzzy >file && git-update-index --add file
@@ -594,6 +601,26 @@ static int ce_flush(SHA_CTX *context, int fd)
 	return 0;
 }
 
+static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
+{
+	/*
+	 * The only thing we care about in this function is to smudge the
+	 * falsely clean entry due to touch-update-touch race, so we leave
+	 * everything else as they are.  We are called for entries whose
+	 * ce_mtime match the index file mtime.
+	 */
+	struct stat st;
+
+	if (lstat(ce->name, &st) < 0)
+		return;
+	if (ce_match_stat_basic(ce, &st))
+		return;
+	if (ce_modified_check_fs(ce, &st)) {
+		/* This is "racily clean"; smudge it */
+		ce->ce_size = htonl(0);
+	}
+}
+
 int write_cache(int newfd, struct cache_entry **cache, int entries)
 {
 	SHA_CTX c;
@@ -616,6 +643,9 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
 		struct cache_entry *ce = cache[i];
 		if (!ce->ce_mode)
 			continue;
+		if (index_file_timestamp &&
+		    index_file_timestamp <= ntohl(ce->ce_mtime.sec))
+			ce_smudge_racily_clean_entry(ce);
 		if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
 			return -1;
 	}
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
index eb175b780f4..e45a9e40e43 100755
--- a/t/t0010-racy-git.sh
+++ b/t/t0010-racy-git.sh
@@ -7,18 +7,27 @@ test_description='racy GIT'
 # This test can give false success if your machine is sufficiently
 # slow or your trial happened to happen on second boundary.
 
-for trial in 0 1 2 3 4 5 6 7 8 9
+for trial in 0 1 2 3 4
 do
 	rm -f .git/index
 	echo frotz >infocom
-	echo xyzzy >activision
-	git update-index --add infocom activision
+	git update-index --add infocom
 	echo xyzzy >infocom
 
 	files=`git diff-files -p`
 	test_expect_success \
-	"Racy GIT trial #$trial" \
+	"Racy GIT trial #$trial part A" \
 	'test "" != "$files"'
+
+	sleep 1
+	echo xyzzy >cornerstone
+	git update-index --add cornerstone
+
+	files=`git diff-files -p`
+	test_expect_success \
+	"Racy GIT trial #$trial part B" \
+	'test "" != "$files"'
+
 done
 
 test_done

From 4b3511b0f8422fd2c5b1b37c9655ae2ce904bca5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 20 Dec 2005 14:18:47 -0800
Subject: [PATCH 41/43] ce_smudge_racily_clean_entry: explain why it works.

This is a tricky code and warrants extra commenting.  I wasted
30 minutes trying to break it until I realized why it works.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 read-cache.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/read-cache.c b/read-cache.c
index afdc2b075b0..c5474d49758 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -616,7 +616,31 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
 	if (ce_match_stat_basic(ce, &st))
 		return;
 	if (ce_modified_check_fs(ce, &st)) {
-		/* This is "racily clean"; smudge it */
+		/* This is "racily clean"; smudge it.  Note that this
+		 * is a tricky code.  At first glance, it may appear
+		 * that it can break with this sequence:
+		 *
+		 * $ echo xyzzy >frotz
+		 * $ git-update-index --add frotz
+		 * $ : >frotz
+		 * $ sleep 3
+		 * $ echo filfre >nitfol
+		 * $ git-update-index --add nitfol
+		 *
+		 * but it does not.  Whe the second update-index runs,
+		 * it notices that the entry "frotz" has the same timestamp
+		 * as index, and if we were to smudge it by resetting its
+		 * size to zero here, then the object name recorded
+		 * in index is the 6-byte file but the cached stat information
+		 * becomes zero --- which would then match what we would
+		 * obtain from the filesystem next time we stat("frotz"). 
+		 *
+		 * However, the second update-index, before calling
+		 * this function, notices that the cached size is 6
+		 * bytes and what is on the filesystem is an empty
+		 * file, and never calls us, so the cached size information
+		 * for "frotz" stays 6 which does not match the filesystem.
+		 */
 		ce->ce_size = htonl(0);
 	}
 }

From a3431febfe241120205472def2a14ef09a4dbe60 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 20 Dec 2005 20:54:28 -0800
Subject: [PATCH 42/43] A shared repository should be writable by members.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/tutorial.txt | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 1683f0bc2db..3a5c56e24ed 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -1625,7 +1625,9 @@ cooperation you are probably more familiar with as well.
 For this, set up a public repository on a machine that is
 reachable via SSH by people with "commit privileges".  Put the
 committers in the same user group and make the repository
-writable by that group.
+writable by that group.  Make sure their umasks are set up to
+allow group members to write into directories other members
+have created.
 
 You, as an individual committer, then:
 

From 41f93a2c903a45167b26c2dc93d45ffa9a9bbd49 Mon Sep 17 00:00:00 2001
From: Linus Torvalds <torvalds@osdl.org>
Date: Tue, 20 Dec 2005 18:13:02 -0800
Subject: [PATCH 43/43] Make "git-send-pack" less verbose by default

It used to make sense to have git-send-pack talk about the things it sent
when (a) it was a new program and (b) nobody had a lot of tags and
branches.

These days, it's just distracting to see tons of

	'refs/tags/xyz': up-to-date
	...

when updating a remote repo.

So shut it up by default, and add a "--verbose" flag for those who really
want to see it.

Also, since this makes he case of everything being up-to-date just totally
silent, make it say "Everything up-to-date" if no refs needed updating.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 send-pack.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/send-pack.c b/send-pack.c
index 6ce0d9f7884..a41bbe5ecfd 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -8,6 +8,7 @@ static const char send_pack_usage[] =
 "git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n"
 "  --all and explicit <head> specification are mutually exclusive.";
 static const char *exec = "git-receive-pack";
+static int verbose = 0;
 static int send_all = 0;
 static int force_update = 0;
 
@@ -206,7 +207,8 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 		if (!ref->peer_ref)
 			continue;
 		if (!memcmp(ref->old_sha1, ref->peer_ref->new_sha1, 20)) {
-			fprintf(stderr, "'%s': up-to-date\n", ref->name);
+			if (verbose)
+				fprintf(stderr, "'%s': up-to-date\n", ref->name);
 			continue;
 		}
 
@@ -270,6 +272,8 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 	packet_flush(out);
 	if (new_refs)
 		pack_objects(out, remote_refs);
+	else
+		fprintf(stderr, "Everything up-to-date\n");
 	close(out);
 	return ret;
 }
@@ -301,6 +305,10 @@ int main(int argc, char **argv)
 				force_update = 1;
 				continue;
 			}
+			if (!strcmp(arg, "--verbose")) {
+				verbose = 1;
+				continue;
+			}
 			usage(send_pack_usage);
 		}
 		if (!dest) {