git-apply: parse index information

Add an new option --show-index-info to git-apply command to
summarize the index information new git-diff outputs.  The
command shows something similar to git-ls-files --stage output
for the pre-change image:

    100644 7be5041...	apply.c
    100644 ec2a161...	cache.h
    ...

Signed-off-by: Junio C Hamano <junkio@cox.net>
diff --git a/apply.c b/apply.c
index 7be5041..155fbe8 100644
--- a/apply.c
+++ b/apply.c
@@ -14,6 +14,7 @@
 //    files that are being modified, but doesn't apply the patch
 //  --stat does just a diffstat, and doesn't actually apply
 //  --show-files shows the directory changes
+//  --show-index-info shows the old and new index info for paths if available.
 //
 static int check_index = 0;
 static int write_index = 0;
@@ -22,8 +23,9 @@
 static int check = 0;
 static int apply = 1;
 static int show_files = 0;
+static int show_index_info = 0;
 static const char apply_usage[] =
-"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] <patch>...";
+"git-apply [--stat] [--summary] [--check] [--index] [--apply] [--show-files] [--show-index-info] <patch>...";
 
 /*
  * For "diff-stat" like behaviour, we keep track of the biggest change
@@ -56,6 +58,8 @@
 	struct fragment *fragments;
 	char *result;
 	unsigned long resultsize;
+	char old_sha1_prefix[41];
+	char new_sha1_prefix[41];
 	struct patch *next;
 };
 
@@ -334,6 +338,38 @@
 	return 0;
 }
 
+static int gitdiff_index(const char *line, struct patch *patch)
+{
+	/* index line is N hexadecimal, "..", N hexadecimal,
+	 * and optional space with octal mode.
+	 */
+	const char *ptr, *eol;
+	int len;
+
+	ptr = strchr(line, '.');
+	if (!ptr || ptr[1] != '.' || 40 <= ptr - line)
+		return 0;
+	len = ptr - line;
+	memcpy(patch->old_sha1_prefix, line, len);
+	patch->old_sha1_prefix[len] = 0;
+
+	line = ptr + 2;
+	ptr = strchr(line, ' ');
+	eol = strchr(line, '\n');
+
+	if (!ptr || eol < ptr)
+		ptr = eol;
+	len = ptr - line;
+
+	if (40 <= len)
+		return 0;
+	memcpy(patch->new_sha1_prefix, line, len);
+	patch->new_sha1_prefix[len] = 0;
+	if (*ptr == ' ')
+		patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+	return 0;
+}
+
 /*
  * This is normal for a diff that doesn't change anything: we'll fall through
  * into the next diff. Tell the parser to break out.
@@ -438,6 +474,7 @@
 			{ "rename to ", gitdiff_renamedst },
 			{ "similarity index ", gitdiff_similarity },
 			{ "dissimilarity index ", gitdiff_dissimilarity },
+			{ "index ", gitdiff_index },
 			{ "", gitdiff_unrecognized },
 		};
 		int i;
@@ -1136,6 +1173,36 @@
 	}
 }
 
+static inline int is_null_sha1(const unsigned char *sha1)
+{
+	return !memcmp(sha1, null_sha1, 20);
+}
+
+static void show_index_list(struct patch *list)
+{
+	struct patch *patch;
+
+	/* Once we start supporting the reverse patch, it may be
+	 * worth showing the new sha1 prefix, but until then...
+	 */
+	for (patch = list; patch; patch = patch->next) {
+		const unsigned char *sha1_ptr;
+		unsigned char sha1[20];
+		const char *name;
+
+		name = patch->old_name ? patch->old_name : patch->new_name;
+		if (patch->is_new)
+			sha1_ptr = null_sha1;
+		else if (get_sha1(patch->old_sha1_prefix, sha1))
+			die("sha1 information is lacking or useless (%s).",
+			    name);
+		else
+			sha1_ptr = sha1;
+		printf("%06o %s	%s\n",patch->old_mode,
+		       sha1_to_hex(sha1_ptr), name);
+	}
+}
+
 static void stat_patch_list(struct patch *patch)
 {
 	int files, adds, dels;
@@ -1476,6 +1543,9 @@
 	if (show_files)
 		show_file_list(list);
 
+	if (show_index_info)
+		show_index_list(list);
+
 	if (diffstat)
 		stat_patch_list(list);
 
@@ -1534,6 +1604,11 @@
 			show_files = 1;
 			continue;
 		}
+		if (!strcmp(arg, "--show-index-info")) {
+			apply = 0;
+			show_index_info = 1;
+			continue;
+		}
 		fd = open(arg, O_RDONLY);
 		if (fd < 0)
 			usage(apply_usage);