GIT 0.99.7
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index fa64c5a..067e4f0 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -76,6 +76,41 @@
 	Flags and parameters to be parsed.
 
 
+SPECIFYING REVISIONS
+--------------------
+
+A revision parameter typically names a commit object.  They use
+what is called an 'extended SHA1' syntax.
+
+* The full SHA1 object name (40-byte hexadecimal string), or
+  a substring of such that is unique within the repository.
+  E.g. dae86e1950b1277e545cee180551750029cfe735 and dae86e both
+  name the same commit object if there are no other object in
+  your repository whose object name starts with dae86e.
+
+* A symbolic ref name.  E.g. 'master' typically means the commit
+  object referenced by $GIT_DIR/refs/heads/master.  If you
+  happen to have both heads/master and tags/master, you can
+  explicitly say 'heads/master' to tell GIT which one you mean.
+
+* A suffix '^' to a revision parameter means the first parent of
+  that commit object.  '^<n>' means the <n>th parent (i.e.
+  'rev^'
+  is equivalent to 'rev^1').  As a special rule,
+  'rev^0' means the commit itself and is used when 'rev' is the
+  object name of a tag object that refers to a commit object.
+
+* A suffix '~<n>' to a revision parameter means the commit
+  object that is the <n>th generation grand-parent of the named
+  commit object, following only the first parent.  I.e. rev~3 is
+  equivalent to rev^^^ which is equivalent to rev^1^1^1.
+
+'git-rev-parse' also accepts a prefix '^' to revision parameter,
+which is passed to 'git-rev-list'.  Two revision parameters
+concatenated with '..' is a short-hand for writing a range
+between them.  I.e. 'r1..r2' is equivalent to saying '^r1 r2'
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and
diff --git a/fetch.c b/fetch.c
index 1d95ce0..af9a013 100644
--- a/fetch.c
+++ b/fetch.c
@@ -174,7 +174,7 @@
 		 * the queue because we needed to fetch it first.
 		 */
 		if (! (obj->flags & TO_SCAN)) {
-			if (fetch(obj->sha1)) {
+			if (!has_sha1_file(obj->sha1) && fetch(obj->sha1)) {
 				report_missing(obj->type
 					       ? obj->type
 					       : "object", obj->sha1);
diff --git a/git-add.sh b/git-add.sh
index 7d91eeb..3d364db 100755
--- a/git-add.sh
+++ b/git-add.sh
@@ -1,2 +1,32 @@
 #!/bin/sh
-git-update-index --add -- "$@"
+
+show_only=
+verbose=
+while : ; do
+  case "$1" in
+    -n)
+	show_only=true
+	verbose=true
+	;;
+    -v)
+	verbose=true
+	;;
+    *)
+	break
+	;;
+  esac
+  shift
+done
+
+GIT_DIR=$(git-rev-parse --git-dir) || exit
+global_exclude=
+if [ -f "$GIT_DIR/info/exclude" ]; then
+   global_exclude="--exclude-from=$GIT_DIR/info/exclude"
+fi
+for i in $(git-ls-files --others \
+	$global_exclude --exclude-per-directory=.gitignore \
+	"$@")
+do
+   [ "$verbose" ] && echo "  $i"
+   [ "$show_only" ] || git-update-index --add -- "$i" || exit
+done
diff --git a/http-fetch.c b/http-fetch.c
index 17051fe..77f530c 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -144,10 +144,11 @@
 	char *url;
 	char *data;
 	int i = 0;
+	int http_specific = 1;
 	if (got_alternates)
 		return 0;
 	data = xmalloc(4096);
-	buffer.size = 4096;
+	buffer.size = 4095;
 	buffer.posn = 0;
 	buffer.buffer = data;
 
@@ -162,6 +163,8 @@
 	curl_easy_setopt(curl, CURLOPT_URL, url);
 
 	if (curl_easy_perform(curl) || !buffer.posn) {
+		http_specific = 0;
+
 		sprintf(url, "%s/objects/info/alternates", base);
 		
 		curl_easy_setopt(curl, CURLOPT_FILE, &buffer);
@@ -173,17 +176,45 @@
 		}
 	}
 
+	data[buffer.posn] = '\0';
+
 	while (i < buffer.posn) {
 		int posn = i;
 		while (posn < buffer.posn && data[posn] != '\n')
 			posn++;
 		if (data[posn] == '\n') {
+			int okay = 0;
+			int serverlen = 0;
+			struct alt_base *newalt;
+			char *target = NULL;
 			if (data[i] == '/') {
-				int serverlen = strchr(base + 8, '/') - base;
-				// skip 'objects' at end
-				char *target = 
-					xmalloc(serverlen + posn - i - 6);
-				struct alt_base *newalt;
+				serverlen = strchr(base + 8, '/') - base;
+				okay = 1;
+			} else if (!memcmp(data + i, "../", 3)) {
+				i += 3;
+				serverlen = strlen(base);
+				while (i + 2 < posn && 
+				       !memcmp(data + i, "../", 3)) {
+					do {
+						serverlen--;
+					} while (serverlen &&
+						 base[serverlen - 1] != '/');
+					i += 3;
+				}
+				// If the server got removed, give up.
+				okay = strchr(base, ':') - base + 3 < 
+					serverlen;
+			} else if (http_specific) {
+				char *colon = strchr(data + i, ':');
+				char *slash = strchr(data + i, '/');
+				if (colon && slash && colon < data + posn &&
+				    slash < data + posn && colon < slash) {
+					okay = 1;
+				}
+			}
+			// skip 'objects' at end
+			if (okay) {
+				target = xmalloc(serverlen + posn - i - 6);
 				strncpy(target, base, serverlen);
 				strncpy(target + serverlen, data + i,
 					posn - i - 7);
@@ -235,7 +266,7 @@
 	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, NULL);
 	
 	if (curl_easy_perform(curl)) {
-		return error("Unable to get pack index %s", url);
+		return -1;
 	}
 
 	while (i < buffer.posn) {
diff --git a/rev-parse.c b/rev-parse.c
index 6d723f9..0f5630f 100644
--- a/rev-parse.c
+++ b/rev-parse.c
@@ -191,6 +191,22 @@
 					puts(prefix);
 				continue;
 			}
+			if (!strcmp(arg, "--git-dir")) {
+				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+				static char cwd[PATH_MAX];
+				if (gitdir) {
+					puts(gitdir);
+					continue;
+				}
+				if (!prefix) {
+					puts(".git");
+					continue;
+				}
+				if (!getcwd(cwd, PATH_MAX))
+					die("unable to get current working directory");
+				printf("%s/.git\n", cwd);
+				continue;
+			}
 			if (verify)
 				die("Needed a single revision");
 			show_flag(arg);
diff --git a/update-index.c b/update-index.c
index d10dfd9..8fe015b 100644
--- a/update-index.c
+++ b/update-index.c
@@ -50,11 +50,20 @@
 		 * case just fine without --force-remove.
 		 */
 		if (status == 0 || (errno == ENOENT || errno == ENOTDIR)) {
-			if (allow_remove)
-				return remove_file_from_cache(path);
+			if (allow_remove) {
+				if (remove_file_from_cache(path))
+					return error("%s: cannot remove from the index",
+					             path);
+				else
+					return 0;
+			} else if (status < 0) {
+				return error("%s: does not exist and --remove not passed",
+				             path);
+			}
 		}
 		if (0 == status)
-			return error("%s: is a directory", path);
+			return error("%s: is a directory - add files inside instead",
+			             path);
 		else
 			return error("lstat(\"%s\"): %s", path,
 				     strerror(errno));
@@ -71,15 +80,17 @@
 	case S_IFREG:
 		fd = open(path, O_RDONLY);
 		if (fd < 0)
-			return -1;
+			return error("open(\"%s\"): %s", path, strerror(errno));
 		if (index_fd(ce->sha1, fd, &st, !info_only, NULL) < 0)
-			return -1;
+			return error("%s: failed to insert into database", path);
 		break;
 	case S_IFLNK:
 		target = xmalloc(st.st_size+1);
 		if (readlink(path, target, st.st_size+1) != st.st_size) {
+			char *errstr = strerror(errno);
 			free(target);
-			return -1;
+			return error("readlink(\"%s\"): %s", path,
+			             errstr);
 		}
 		if (info_only) {
 			unsigned char hdr[50];
@@ -87,15 +98,18 @@
 			write_sha1_file_prepare(target, st.st_size, "blob",
 						ce->sha1, hdr, &hdrlen);
 		} else if (write_sha1_file(target, st.st_size, "blob", ce->sha1))
-			return -1;
+			return error("%s: failed to insert into database", path);
 		free(target);
 		break;
 	default:
-		return -1;
+		return error("%s: unsupported file type", path);
 	}
 	option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
 	option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
-	return add_cache_entry(ce, option);
+	if (add_cache_entry(ce, option))
+		return error("%s: cannot add to the index - missing --add option?",
+			     path);
+	return 0;
 }
 
 static int compare_data(struct cache_entry *ce, struct stat *st)
@@ -393,11 +407,11 @@
 		}
 		if (force_remove) {
 			if (remove_file_from_cache(path))
-				die("git-update-index: --force-remove cannot remove %s", path);
+				die("git-update-index: unable to remove %s", path);
 			continue;
 		}
 		if (add_file_to_cache(path))
-			die("Unable to add %s to database; maybe you want to use --add option?", path);
+			die("Unable to process file %s", path);
 	}
 	if (write_cache(newfd, active_cache, active_nr) ||
 	    commit_index_file(&cache_file))