Merge branch 'jc/builtin-n-tar-tree' into next
authorJunio C Hamano <junkio@cox.net>
Tue, 23 May 2006 21:52:45 +0000 (14:52 -0700)
committerJunio C Hamano <junkio@cox.net>
Tue, 23 May 2006 21:52:45 +0000 (14:52 -0700)
* jc/builtin-n-tar-tree:
  Builtin git-diff-files, git-diff-index, git-diff-stages, and git-diff-tree.
  Builtin git-show-branch.
  Builtin git-apply.
  Builtin git-commit-tree.
  Builtin git-read-tree.
  Builtin git-tar-tree.
  Builtin git-ls-tree.
  Builtin git-ls-files.

1  2 
Makefile
builtin-apply.c
builtin-ls-files.c
builtin-read-tree.c
builtin-show-branch.c
builtin.h
git.c

diff --cc Makefile
+++ b/Makefile
@@@ -170,9 -168,12 +168,13 @@@ PROGRAMS = 
  
  BUILT_INS = git-log$X git-whatchanged$X git-show$X \
        git-count-objects$X git-diff$X git-push$X \
 -      git-grep$X git-rev-list$X git-check-ref-format$X \
 -      git-init-db$X git-ls-files$X git-ls-tree$X \
 +      git-grep$X git-add$X git-rm$X git-rev-list$X \
 +      git-check-ref-format$X \
-       git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X
++      git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
++      git-ls-files$X git-ls-tree$X \
+       git-read-tree$X git-commit-tree$X \
+       git-apply$X git-show-branch$X git-diff-files$X \
 -      git-diff-index$X git-diff-stages$X git-diff-tree$X \
 -      git-tar-tree$X git-upload-tar$X
++      git-diff-index$X git-diff-stages$X git-diff-tree$X
  
  # what 'all' will build and 'install' will install, in gitexecdir
  ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@@ -220,8 -221,12 +222,13 @@@ LIB_OBJS = 
  
  BUILTIN_OBJS = \
        builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
 -      builtin-grep.o builtin-rev-list.o builtin-check-ref-format.o \
 -      builtin-init-db.o builtin-ls-files.o builtin-ls-tree.o \
 +      builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
-       builtin-rm.o builtin-init-db.o builtin-tar-tree.o builtin-upload-tar.o
++      builtin-rm.o builtin-init-db.o \
++      builtin-tar-tree.o builtin-upload-tar.o \
++      builtin-ls-files.o builtin-ls-tree.o \
+       builtin-read-tree.o builtin-commit-tree.o \
+       builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
 -      builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
 -      builtin-tar-tree.o builtin-upload-tar.o
++      builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o
  
  GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
  LIBS = $(GITLIBS) -lz
diff --cc builtin-apply.c
index 0000000,4056b9d..f16c753
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,2300 +1,2303 @@@
 -      create_one_file(path, mode, buf, size); 
+ /*
+  * apply.c
+  *
+  * Copyright (C) Linus Torvalds, 2005
+  *
+  * This applies patches on top of some (arbitrary) version of the SCM.
+  *
+  */
+ #include <fnmatch.h>
+ #include "cache.h"
++#include "cache-tree.h"
+ #include "quote.h"
+ #include "blob.h"
+ #include "delta.h"
+ #include "builtin.h"
+ //  --check turns on checking that the working tree matches the
+ //    files that are being modified, but doesn't apply the patch
+ //  --stat does just a diffstat, and doesn't actually apply
+ //  --numstat does numeric diffstat, and doesn't actually apply
+ //  --index-info shows the old and new index info for paths if available.
+ //  --index updates the cache as well.
+ //  --cached updates only the cache without ever touching the working tree.
+ //
+ static const char *prefix;
+ static int prefix_length = -1;
+ static int newfd = -1;
+ static int p_value = 1;
+ static int allow_binary_replacement = 0;
+ static int check_index = 0;
+ static int write_index = 0;
+ static int cached = 0;
+ static int diffstat = 0;
+ static int numstat = 0;
+ static int summary = 0;
+ static int check = 0;
+ static int apply = 1;
+ static int no_add = 0;
+ static int show_index_info = 0;
+ static int line_termination = '\n';
+ static unsigned long p_context = -1;
+ static const char apply_usage[] =
+ "git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
+ static enum whitespace_eol {
+       nowarn_whitespace,
+       warn_on_whitespace,
+       error_on_whitespace,
+       strip_whitespace,
+ } new_whitespace = warn_on_whitespace;
+ static int whitespace_error = 0;
+ static int squelch_whitespace_errors = 5;
+ static int applied_after_stripping = 0;
+ static const char *patch_input_file = NULL;
+ static void parse_whitespace_option(const char *option)
+ {
+       if (!option) {
+               new_whitespace = warn_on_whitespace;
+               return;
+       }
+       if (!strcmp(option, "warn")) {
+               new_whitespace = warn_on_whitespace;
+               return;
+       }
+       if (!strcmp(option, "nowarn")) {
+               new_whitespace = nowarn_whitespace;
+               return;
+       }
+       if (!strcmp(option, "error")) {
+               new_whitespace = error_on_whitespace;
+               return;
+       }
+       if (!strcmp(option, "error-all")) {
+               new_whitespace = error_on_whitespace;
+               squelch_whitespace_errors = 0;
+               return;
+       }
+       if (!strcmp(option, "strip")) {
+               new_whitespace = strip_whitespace;
+               return;
+       }
+       die("unrecognized whitespace option '%s'", option);
+ }
+ static void set_default_whitespace_mode(const char *whitespace_option)
+ {
+       if (!whitespace_option && !apply_default_whitespace) {
+               new_whitespace = (apply
+                                 ? warn_on_whitespace
+                                 : nowarn_whitespace);
+       }
+ }
+ /*
+  * For "diff-stat" like behaviour, we keep track of the biggest change
+  * we've seen, and the longest filename. That allows us to do simple
+  * scaling.
+  */
+ static int max_change, max_len;
+ /*
+  * Various "current state", notably line numbers and what
+  * file (and how) we're patching right now.. The "is_xxxx"
+  * things are flags, where -1 means "don't know yet".
+  */
+ static int linenr = 1;
+ struct fragment {
+       unsigned long leading, trailing;
+       unsigned long oldpos, oldlines;
+       unsigned long newpos, newlines;
+       const char *patch;
+       int size;
+       struct fragment *next;
+ };
+ struct patch {
+       char *new_name, *old_name, *def_name;
+       unsigned int old_mode, new_mode;
+       int is_rename, is_copy, is_new, is_delete, is_binary;
+ #define BINARY_DELTA_DEFLATED 1
+ #define BINARY_LITERAL_DEFLATED 2
+       unsigned long deflate_origlen;
+       int lines_added, lines_deleted;
+       int score;
+       struct fragment *fragments;
+       char *result;
+       unsigned long resultsize;
+       char old_sha1_prefix[41];
+       char new_sha1_prefix[41];
+       struct patch *next;
+ };
+ #define CHUNKSIZE (8192)
+ #define SLOP (16)
+ static void *read_patch_file(int fd, unsigned long *sizep)
+ {
+       unsigned long size = 0, alloc = CHUNKSIZE;
+       void *buffer = xmalloc(alloc);
+       for (;;) {
+               int nr = alloc - size;
+               if (nr < 1024) {
+                       alloc += CHUNKSIZE;
+                       buffer = xrealloc(buffer, alloc);
+                       nr = alloc - size;
+               }
+               nr = xread(fd, buffer + size, nr);
+               if (!nr)
+                       break;
+               if (nr < 0)
+                       die("git-apply: read returned %s", strerror(errno));
+               size += nr;
+       }
+       *sizep = size;
+       /*
+        * Make sure that we have some slop in the buffer
+        * so that we can do speculative "memcmp" etc, and
+        * see to it that it is NUL-filled.
+        */
+       if (alloc < size + SLOP)
+               buffer = xrealloc(buffer, size + SLOP);
+       memset(buffer + size, 0, SLOP);
+       return buffer;
+ }
+ static unsigned long linelen(const char *buffer, unsigned long size)
+ {
+       unsigned long len = 0;
+       while (size--) {
+               len++;
+               if (*buffer++ == '\n')
+                       break;
+       }
+       return len;
+ }
+ static int is_dev_null(const char *str)
+ {
+       return !memcmp("/dev/null", str, 9) && isspace(str[9]);
+ }
+ #define TERM_SPACE    1
+ #define TERM_TAB      2
+ static int name_terminate(const char *name, int namelen, int c, int terminate)
+ {
+       if (c == ' ' && !(terminate & TERM_SPACE))
+               return 0;
+       if (c == '\t' && !(terminate & TERM_TAB))
+               return 0;
+       return 1;
+ }
+ static char * find_name(const char *line, char *def, int p_value, int terminate)
+ {
+       int len;
+       const char *start = line;
+       char *name;
+       if (*line == '"') {
+               /* Proposed "new-style" GNU patch/diff format; see
+                * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+                */
+               name = unquote_c_style(line, NULL);
+               if (name) {
+                       char *cp = name;
+                       while (p_value) {
+                               cp = strchr(name, '/');
+                               if (!cp)
+                                       break;
+                               cp++;
+                               p_value--;
+                       }
+                       if (cp) {
+                               /* name can later be freed, so we need
+                                * to memmove, not just return cp
+                                */
+                               memmove(name, cp, strlen(cp) + 1);
+                               free(def);
+                               return name;
+                       }
+                       else {
+                               free(name);
+                               name = NULL;
+                       }
+               }
+       }
+       for (;;) {
+               char c = *line;
+               if (isspace(c)) {
+                       if (c == '\n')
+                               break;
+                       if (name_terminate(start, line-start, c, terminate))
+                               break;
+               }
+               line++;
+               if (c == '/' && !--p_value)
+                       start = line;
+       }
+       if (!start)
+               return def;
+       len = line - start;
+       if (!len)
+               return def;
+       /*
+        * Generally we prefer the shorter name, especially
+        * if the other one is just a variation of that with
+        * something else tacked on to the end (ie "file.orig"
+        * or "file~").
+        */
+       if (def) {
+               int deflen = strlen(def);
+               if (deflen < len && !strncmp(start, def, deflen))
+                       return def;
+       }
+       name = xmalloc(len + 1);
+       memcpy(name, start, len);
+       name[len] = 0;
+       free(def);
+       return name;
+ }
+ /*
+  * Get the name etc info from the --/+++ lines of a traditional patch header
+  *
+  * NOTE! This hardcodes "-p1" behaviour in filename detection.
+  *
+  * FIXME! The end-of-filename heuristics are kind of screwy. For existing
+  * files, we can happily check the index for a match, but for creating a
+  * new file we should try to match whatever "patch" does. I have no idea.
+  */
+ static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
+ {
+       char *name;
+       first += 4;     // skip "--- "
+       second += 4;    // skip "+++ "
+       if (is_dev_null(first)) {
+               patch->is_new = 1;
+               patch->is_delete = 0;
+               name = find_name(second, NULL, p_value, TERM_SPACE | TERM_TAB);
+               patch->new_name = name;
+       } else if (is_dev_null(second)) {
+               patch->is_new = 0;
+               patch->is_delete = 1;
+               name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
+               patch->old_name = name;
+       } else {
+               name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
+               name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
+               patch->old_name = patch->new_name = name;
+       }
+       if (!name)
+               die("unable to find filename in patch at line %d", linenr);
+ }
+ static int gitdiff_hdrend(const char *line, struct patch *patch)
+ {
+       return -1;
+ }
+ /*
+  * We're anal about diff header consistency, to make
+  * sure that we don't end up having strange ambiguous
+  * patches floating around.
+  *
+  * As a result, gitdiff_{old|new}name() will check
+  * their names against any previous information, just
+  * to make sure..
+  */
+ static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
+ {
+       if (!orig_name && !isnull)
+               return find_name(line, NULL, 1, 0);
+       if (orig_name) {
+               int len;
+               const char *name;
+               char *another;
+               name = orig_name;
+               len = strlen(name);
+               if (isnull)
+                       die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
+               another = find_name(line, NULL, 1, 0);
+               if (!another || memcmp(another, name, len))
+                       die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
+               free(another);
+               return orig_name;
+       }
+       else {
+               /* expect "/dev/null" */
+               if (memcmp("/dev/null", line, 9) || line[9] != '\n')
+                       die("git-apply: bad git-diff - expected /dev/null on line %d", linenr);
+               return NULL;
+       }
+ }
+ static int gitdiff_oldname(const char *line, struct patch *patch)
+ {
+       patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, "old");
+       return 0;
+ }
+ static int gitdiff_newname(const char *line, struct patch *patch)
+ {
+       patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, "new");
+       return 0;
+ }
+ static int gitdiff_oldmode(const char *line, struct patch *patch)
+ {
+       patch->old_mode = strtoul(line, NULL, 8);
+       return 0;
+ }
+ static int gitdiff_newmode(const char *line, struct patch *patch)
+ {
+       patch->new_mode = strtoul(line, NULL, 8);
+       return 0;
+ }
+ static int gitdiff_delete(const char *line, struct patch *patch)
+ {
+       patch->is_delete = 1;
+       patch->old_name = patch->def_name;
+       return gitdiff_oldmode(line, patch);
+ }
+ static int gitdiff_newfile(const char *line, struct patch *patch)
+ {
+       patch->is_new = 1;
+       patch->new_name = patch->def_name;
+       return gitdiff_newmode(line, patch);
+ }
+ static int gitdiff_copysrc(const char *line, struct patch *patch)
+ {
+       patch->is_copy = 1;
+       patch->old_name = find_name(line, NULL, 0, 0);
+       return 0;
+ }
+ static int gitdiff_copydst(const char *line, struct patch *patch)
+ {
+       patch->is_copy = 1;
+       patch->new_name = find_name(line, NULL, 0, 0);
+       return 0;
+ }
+ static int gitdiff_renamesrc(const char *line, struct patch *patch)
+ {
+       patch->is_rename = 1;
+       patch->old_name = find_name(line, NULL, 0, 0);
+       return 0;
+ }
+ static int gitdiff_renamedst(const char *line, struct patch *patch)
+ {
+       patch->is_rename = 1;
+       patch->new_name = find_name(line, NULL, 0, 0);
+       return 0;
+ }
+ static int gitdiff_similarity(const char *line, struct patch *patch)
+ {
+       if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
+               patch->score = 0;
+       return 0;
+ }
+ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
+ {
+       if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
+               patch->score = 0;
+       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.
+  */
+ static int gitdiff_unrecognized(const char *line, struct patch *patch)
+ {
+       return -1;
+ }
+ static const char *stop_at_slash(const char *line, int llen)
+ {
+       int i;
+       for (i = 0; i < llen; i++) {
+               int ch = line[i];
+               if (ch == '/')
+                       return line + i;
+       }
+       return NULL;
+ }
+ /* This is to extract the same name that appears on "diff --git"
+  * line.  We do not find and return anything if it is a rename
+  * patch, and it is OK because we will find the name elsewhere.
+  * We need to reliably find name only when it is mode-change only,
+  * creation or deletion of an empty file.  In any of these cases,
+  * both sides are the same name under a/ and b/ respectively.
+  */
+ static char *git_header_name(char *line, int llen)
+ {
+       int len;
+       const char *name;
+       const char *second = NULL;
+       line += strlen("diff --git ");
+       llen -= strlen("diff --git ");
+       if (*line == '"') {
+               const char *cp;
+               char *first = unquote_c_style(line, &second);
+               if (!first)
+                       return NULL;
+               /* advance to the first slash */
+               cp = stop_at_slash(first, strlen(first));
+               if (!cp || cp == first) {
+                       /* we do not accept absolute paths */
+               free_first_and_fail:
+                       free(first);
+                       return NULL;
+               }
+               len = strlen(cp+1);
+               memmove(first, cp+1, len+1); /* including NUL */
+               /* second points at one past closing dq of name.
+                * find the second name.
+                */
+               while ((second < line + llen) && isspace(*second))
+                       second++;
+               if (line + llen <= second)
+                       goto free_first_and_fail;
+               if (*second == '"') {
+                       char *sp = unquote_c_style(second, NULL);
+                       if (!sp)
+                               goto free_first_and_fail;
+                       cp = stop_at_slash(sp, strlen(sp));
+                       if (!cp || cp == sp) {
+                       free_both_and_fail:
+                               free(sp);
+                               goto free_first_and_fail;
+                       }
+                       /* They must match, otherwise ignore */
+                       if (strcmp(cp+1, first))
+                               goto free_both_and_fail;
+                       free(sp);
+                       return first;
+               }
+               /* unquoted second */
+               cp = stop_at_slash(second, line + llen - second);
+               if (!cp || cp == second)
+                       goto free_first_and_fail;
+               cp++;
+               if (line + llen - cp != len + 1 ||
+                   memcmp(first, cp, len))
+                       goto free_first_and_fail;
+               return first;
+       }
+       /* unquoted first name */
+       name = stop_at_slash(line, llen);
+       if (!name || name == line)
+               return NULL;
+       name++;
+       /* since the first name is unquoted, a dq if exists must be
+        * the beginning of the second name.
+        */
+       for (second = name; second < line + llen; second++) {
+               if (*second == '"') {
+                       const char *cp = second;
+                       const char *np;
+                       char *sp = unquote_c_style(second, NULL);
+                       if (!sp)
+                               return NULL;
+                       np = stop_at_slash(sp, strlen(sp));
+                       if (!np || np == sp) {
+                       free_second_and_fail:
+                               free(sp);
+                               return NULL;
+                       }
+                       np++;
+                       len = strlen(np);
+                       if (len < cp - name &&
+                           !strncmp(np, name, len) &&
+                           isspace(name[len])) {
+                               /* Good */
+                               memmove(sp, np, len + 1);
+                               return sp;
+                       }
+                       goto free_second_and_fail;
+               }
+       }
+       /*
+        * Accept a name only if it shows up twice, exactly the same
+        * form.
+        */
+       for (len = 0 ; ; len++) {
+               char c = name[len];
+               switch (c) {
+               default:
+                       continue;
+               case '\n':
+                       return NULL;
+               case '\t': case ' ':
+                       second = name+len;
+                       for (;;) {
+                               char c = *second++;
+                               if (c == '\n')
+                                       return NULL;
+                               if (c == '/')
+                                       break;
+                       }
+                       if (second[len] == '\n' && !memcmp(name, second, len)) {
+                               char *ret = xmalloc(len + 1);
+                               memcpy(ret, name, len);
+                               ret[len] = 0;
+                               return ret;
+                       }
+               }
+       }
+       return NULL;
+ }
+ /* Verify that we recognize the lines following a git header */
+ static int parse_git_header(char *line, int len, unsigned int size, struct patch *patch)
+ {
+       unsigned long offset;
+       /* A git diff has explicit new/delete information, so we don't guess */
+       patch->is_new = 0;
+       patch->is_delete = 0;
+       /*
+        * Some things may not have the old name in the
+        * rest of the headers anywhere (pure mode changes,
+        * or removing or adding empty files), so we get
+        * the default name from the header.
+        */
+       patch->def_name = git_header_name(line, len);
+       line += len;
+       size -= len;
+       linenr++;
+       for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
+               static const struct opentry {
+                       const char *str;
+                       int (*fn)(const char *, struct patch *);
+               } optable[] = {
+                       { "@@ -", gitdiff_hdrend },
+                       { "--- ", gitdiff_oldname },
+                       { "+++ ", gitdiff_newname },
+                       { "old mode ", gitdiff_oldmode },
+                       { "new mode ", gitdiff_newmode },
+                       { "deleted file mode ", gitdiff_delete },
+                       { "new file mode ", gitdiff_newfile },
+                       { "copy from ", gitdiff_copysrc },
+                       { "copy to ", gitdiff_copydst },
+                       { "rename old ", gitdiff_renamesrc },
+                       { "rename new ", gitdiff_renamedst },
+                       { "rename from ", gitdiff_renamesrc },
+                       { "rename to ", gitdiff_renamedst },
+                       { "similarity index ", gitdiff_similarity },
+                       { "dissimilarity index ", gitdiff_dissimilarity },
+                       { "index ", gitdiff_index },
+                       { "", gitdiff_unrecognized },
+               };
+               int i;
+               len = linelen(line, size);
+               if (!len || line[len-1] != '\n')
+                       break;
+               for (i = 0; i < ARRAY_SIZE(optable); i++) {
+                       const struct opentry *p = optable + i;
+                       int oplen = strlen(p->str);
+                       if (len < oplen || memcmp(p->str, line, oplen))
+                               continue;
+                       if (p->fn(line + oplen, patch) < 0)
+                               return offset;
+                       break;
+               }
+       }
+       return offset;
+ }
+ static int parse_num(const char *line, unsigned long *p)
+ {
+       char *ptr;
+       if (!isdigit(*line))
+               return 0;
+       *p = strtoul(line, &ptr, 10);
+       return ptr - line;
+ }
+ static int parse_range(const char *line, int len, int offset, const char *expect,
+                       unsigned long *p1, unsigned long *p2)
+ {
+       int digits, ex;
+       if (offset < 0 || offset >= len)
+               return -1;
+       line += offset;
+       len -= offset;
+       digits = parse_num(line, p1);
+       if (!digits)
+               return -1;
+       offset += digits;
+       line += digits;
+       len -= digits;
+       *p2 = 1;
+       if (*line == ',') {
+               digits = parse_num(line+1, p2);
+               if (!digits)
+                       return -1;
+               offset += digits+1;
+               line += digits+1;
+               len -= digits+1;
+       }
+       ex = strlen(expect);
+       if (ex > len)
+               return -1;
+       if (memcmp(line, expect, ex))
+               return -1;
+       return offset + ex;
+ }
+ /*
+  * Parse a unified diff fragment header of the
+  * form "@@ -a,b +c,d @@"
+  */
+ static int parse_fragment_header(char *line, int len, struct fragment *fragment)
+ {
+       int offset;
+       if (!len || line[len-1] != '\n')
+               return -1;
+       /* Figure out the number of lines in a fragment */
+       offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines);
+       offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines);
+       return offset;
+ }
+ static int find_header(char *line, unsigned long size, int *hdrsize, struct patch *patch)
+ {
+       unsigned long offset, len;
+       patch->is_rename = patch->is_copy = 0;
+       patch->is_new = patch->is_delete = -1;
+       patch->old_mode = patch->new_mode = 0;
+       patch->old_name = patch->new_name = NULL;
+       for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
+               unsigned long nextlen;
+               len = linelen(line, size);
+               if (!len)
+                       break;
+               /* Testing this early allows us to take a few shortcuts.. */
+               if (len < 6)
+                       continue;
+               /*
+                * Make sure we don't find any unconnected patch fragmants.
+                * That's a sign that we didn't find a header, and that a
+                * patch has become corrupted/broken up.
+                */
+               if (!memcmp("@@ -", line, 4)) {
+                       struct fragment dummy;
+                       if (parse_fragment_header(line, len, &dummy) < 0)
+                               continue;
+                       error("patch fragment without header at line %d: %.*s", linenr, (int)len-1, line);
+               }
+               if (size < len + 6)
+                       break;
+               /*
+                * Git patch? It might not have a real patch, just a rename
+                * or mode change, so we handle that specially
+                */
+               if (!memcmp("diff --git ", line, 11)) {
+                       int git_hdr_len = parse_git_header(line, len, size, patch);
+                       if (git_hdr_len <= len)
+                               continue;
+                       if (!patch->old_name && !patch->new_name) {
+                               if (!patch->def_name)
+                                       die("git diff header lacks filename information (line %d)", linenr);
+                               patch->old_name = patch->new_name = patch->def_name;
+                       }
+                       *hdrsize = git_hdr_len;
+                       return offset;
+               }
+               /** --- followed by +++ ? */
+               if (memcmp("--- ", line,  4) || memcmp("+++ ", line + len, 4))
+                       continue;
+               /*
+                * We only accept unified patches, so we want it to
+                * at least have "@@ -a,b +c,d @@\n", which is 14 chars
+                * minimum
+                */
+               nextlen = linelen(line + len, size - len);
+               if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
+                       continue;
+               /* Ok, we'll consider it a patch */
+               parse_traditional_patch(line, line+len, patch);
+               *hdrsize = len + nextlen;
+               linenr += 2;
+               return offset;
+       }
+       return -1;
+ }
+ /*
+  * Parse a unified diff. Note that this really needs
+  * to parse each fragment separately, since the only
+  * way to know the difference between a "---" that is
+  * part of a patch, and a "---" that starts the next
+  * patch is to look at the line counts..
+  */
+ static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
+ {
+       int added, deleted;
+       int len = linelen(line, size), offset;
+       unsigned long oldlines, newlines;
+       unsigned long leading, trailing;
+       offset = parse_fragment_header(line, len, fragment);
+       if (offset < 0)
+               return -1;
+       oldlines = fragment->oldlines;
+       newlines = fragment->newlines;
+       leading = 0;
+       trailing = 0;
+       if (patch->is_new < 0) {
+               patch->is_new =  !oldlines;
+               if (!oldlines)
+                       patch->old_name = NULL;
+       }
+       if (patch->is_delete < 0) {
+               patch->is_delete = !newlines;
+               if (!newlines)
+                       patch->new_name = NULL;
+       }
+       if (patch->is_new && oldlines)
+               return error("new file depends on old contents");
+       if (patch->is_delete != !newlines) {
+               if (newlines)
+                       return error("deleted file still has contents");
+               fprintf(stderr, "** warning: file %s becomes empty but is not deleted\n", patch->new_name);
+       }
+       /* Parse the thing.. */
+       line += len;
+       size -= len;
+       linenr++;
+       added = deleted = 0;
+       for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
+               if (!oldlines && !newlines)
+                       break;
+               len = linelen(line, size);
+               if (!len || line[len-1] != '\n')
+                       return -1;
+               switch (*line) {
+               default:
+                       return -1;
+               case ' ':
+                       oldlines--;
+                       newlines--;
+                       if (!deleted && !added)
+                               leading++;
+                       trailing++;
+                       break;
+               case '-':
+                       deleted++;
+                       oldlines--;
+                       trailing = 0;
+                       break;
+               case '+':
+                       /*
+                        * We know len is at least two, since we have a '+' and
+                        * we checked that the last character was a '\n' above.
+                        * That is, an addition of an empty line would check
+                        * the '+' here.  Sneaky...
+                        */
+                       if ((new_whitespace != nowarn_whitespace) &&
+                           isspace(line[len-2])) {
+                               whitespace_error++;
+                               if (squelch_whitespace_errors &&
+                                   squelch_whitespace_errors <
+                                   whitespace_error)
+                                       ;
+                               else {
+                                       fprintf(stderr, "Adds trailing whitespace.\n%s:%d:%.*s\n",
+                                               patch_input_file,
+                                               linenr, len-2, line+1);
+                               }
+                       }
+                       added++;
+                       newlines--;
+                       trailing = 0;
+                       break;
+                 /* We allow "\ No newline at end of file". Depending
+                  * on locale settings when the patch was produced we
+                  * don't know what this line looks like. The only
+                  * thing we do know is that it begins with "\ ".
+                * Checking for 12 is just for sanity check -- any
+                * l10n of "\ No newline..." is at least that long.
+                */
+               case '\\':
+                       if (len < 12 || memcmp(line, "\\ ", 2))
+                               return -1;
+                       break;
+               }
+       }
+       if (oldlines || newlines)
+               return -1;
+       fragment->leading = leading;
+       fragment->trailing = trailing;
+       /* If a fragment ends with an incomplete line, we failed to include
+        * it in the above loop because we hit oldlines == newlines == 0
+        * before seeing it.
+        */
+       if (12 < size && !memcmp(line, "\\ ", 2))
+               offset += linelen(line, size);
+       patch->lines_added += added;
+       patch->lines_deleted += deleted;
+       return offset;
+ }
+ static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
+ {
+       unsigned long offset = 0;
+       struct fragment **fragp = &patch->fragments;
+       while (size > 4 && !memcmp(line, "@@ -", 4)) {
+               struct fragment *fragment;
+               int len;
+               fragment = xcalloc(1, sizeof(*fragment));
+               len = parse_fragment(line, size, patch, fragment);
+               if (len <= 0)
+                       die("corrupt patch at line %d", linenr);
+               fragment->patch = line;
+               fragment->size = len;
+               *fragp = fragment;
+               fragp = &fragment->next;
+               offset += len;
+               line += len;
+               size -= len;
+       }
+       return offset;
+ }
+ static inline int metadata_changes(struct patch *patch)
+ {
+       return  patch->is_rename > 0 ||
+               patch->is_copy > 0 ||
+               patch->is_new > 0 ||
+               patch->is_delete ||
+               (patch->old_mode && patch->new_mode &&
+                patch->old_mode != patch->new_mode);
+ }
+ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
+ {
+       /* We have read "GIT binary patch\n"; what follows is a line
+        * that says the patch method (currently, either "deflated
+        * literal" or "deflated delta") and the length of data before
+        * deflating; a sequence of 'length-byte' followed by base-85
+        * encoded data follows.
+        *
+        * Each 5-byte sequence of base-85 encodes up to 4 bytes,
+        * and we would limit the patch line to 66 characters,
+        * so one line can fit up to 13 groups that would decode
+        * to 52 bytes max.  The length byte 'A'-'Z' corresponds
+        * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
+        * The end of binary is signalled with an empty line.
+        */
+       int llen, used;
+       struct fragment *fragment;
+       char *data = NULL;
+       patch->fragments = fragment = xcalloc(1, sizeof(*fragment));
+       /* Grab the type of patch */
+       llen = linelen(buffer, size);
+       used = llen;
+       linenr++;
+       if (!strncmp(buffer, "delta ", 6)) {
+               patch->is_binary = BINARY_DELTA_DEFLATED;
+               patch->deflate_origlen = strtoul(buffer + 6, NULL, 10);
+       }
+       else if (!strncmp(buffer, "literal ", 8)) {
+               patch->is_binary = BINARY_LITERAL_DEFLATED;
+               patch->deflate_origlen = strtoul(buffer + 8, NULL, 10);
+       }
+       else
+               return error("unrecognized binary patch at line %d: %.*s",
+                            linenr-1, llen-1, buffer);
+       buffer += llen;
+       while (1) {
+               int byte_length, max_byte_length, newsize;
+               llen = linelen(buffer, size);
+               used += llen;
+               linenr++;
+               if (llen == 1)
+                       break;
+               /* Minimum line is "A00000\n" which is 7-byte long,
+                * and the line length must be multiple of 5 plus 2.
+                */
+               if ((llen < 7) || (llen-2) % 5)
+                       goto corrupt;
+               max_byte_length = (llen - 2) / 5 * 4;
+               byte_length = *buffer;
+               if ('A' <= byte_length && byte_length <= 'Z')
+                       byte_length = byte_length - 'A' + 1;
+               else if ('a' <= byte_length && byte_length <= 'z')
+                       byte_length = byte_length - 'a' + 27;
+               else
+                       goto corrupt;
+               /* if the input length was not multiple of 4, we would
+                * have filler at the end but the filler should never
+                * exceed 3 bytes
+                */
+               if (max_byte_length < byte_length ||
+                   byte_length <= max_byte_length - 4)
+                       goto corrupt;
+               newsize = fragment->size + byte_length;
+               data = xrealloc(data, newsize);
+               if (decode_85(data + fragment->size,
+                             buffer + 1,
+                             byte_length))
+                       goto corrupt;
+               fragment->size = newsize;
+               buffer += llen;
+               size -= llen;
+       }
+       fragment->patch = data;
+       return used;
+  corrupt:
+       return error("corrupt binary patch at line %d: %.*s",
+                    linenr-1, llen-1, buffer);
+ }
+ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
+ {
+       int hdrsize, patchsize;
+       int offset = find_header(buffer, size, &hdrsize, patch);
+       if (offset < 0)
+               return offset;
+       patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
+       if (!patchsize) {
+               static const char *binhdr[] = {
+                       "Binary files ",
+                       "Files ",
+                       NULL,
+               };
+               static const char git_binary[] = "GIT binary patch\n";
+               int i;
+               int hd = hdrsize + offset;
+               unsigned long llen = linelen(buffer + hd, size - hd);
+               if (llen == sizeof(git_binary) - 1 &&
+                   !memcmp(git_binary, buffer + hd, llen)) {
+                       int used;
+                       linenr++;
+                       used = parse_binary(buffer + hd + llen,
+                                           size - hd - llen, patch);
+                       if (used)
+                               patchsize = used + llen;
+                       else
+                               patchsize = 0;
+               }
+               else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
+                       for (i = 0; binhdr[i]; i++) {
+                               int len = strlen(binhdr[i]);
+                               if (len < size - hd &&
+                                   !memcmp(binhdr[i], buffer + hd, len)) {
+                                       linenr++;
+                                       patch->is_binary = 1;
+                                       patchsize = llen;
+                                       break;
+                               }
+                       }
+               }
+               /* Empty patch cannot be applied if:
+                * - it is a binary patch and we do not do binary_replace, or
+                * - text patch without metadata change
+                */
+               if ((apply || check) &&
+                   (patch->is_binary
+                    ? !allow_binary_replacement
+                    : !metadata_changes(patch)))
+                       die("patch with only garbage at line %d", linenr);
+       }
+       return offset + hdrsize + patchsize;
+ }
+ static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+ static const char minuses[]= "----------------------------------------------------------------------";
+ static void show_stats(struct patch *patch)
+ {
+       const char *prefix = "";
+       char *name = patch->new_name;
+       char *qname = NULL;
+       int len, max, add, del, total;
+       if (!name)
+               name = patch->old_name;
+       if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
+               qname = xmalloc(len + 1);
+               quote_c_style(name, qname, NULL, 0);
+               name = qname;
+       }
+       /*
+        * "scale" the filename
+        */
+       len = strlen(name);
+       max = max_len;
+       if (max > 50)
+               max = 50;
+       if (len > max) {
+               char *slash;
+               prefix = "...";
+               max -= 3;
+               name += len - max;
+               slash = strchr(name, '/');
+               if (slash)
+                       name = slash;
+       }
+       len = max;
+       /*
+        * scale the add/delete
+        */
+       max = max_change;
+       if (max + len > 70)
+               max = 70 - len;
+       add = patch->lines_added;
+       del = patch->lines_deleted;
+       total = add + del;
+       if (max_change > 0) {
+               total = (total * max + max_change / 2) / max_change;
+               add = (add * max + max_change / 2) / max_change;
+               del = total - add;
+       }
+       if (patch->is_binary)
+               printf(" %s%-*s |  Bin\n", prefix, len, name);
+       else
+               printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+                      len, name, patch->lines_added + patch->lines_deleted,
+                      add, pluses, del, minuses);
+       if (qname)
+               free(qname);
+ }
+ static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
+ {
+       int fd;
+       unsigned long got;
+       switch (st->st_mode & S_IFMT) {
+       case S_IFLNK:
+               return readlink(path, buf, size);
+       case S_IFREG:
+               fd = open(path, O_RDONLY);
+               if (fd < 0)
+                       return error("unable to open %s", path);
+               got = 0;
+               for (;;) {
+                       int ret = xread(fd, buf + got, size - got);
+                       if (ret <= 0)
+                               break;
+                       got += ret;
+               }
+               close(fd);
+               return got;
+       default:
+               return -1;
+       }
+ }
+ static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines)
+ {
+       int i;
+       unsigned long start, backwards, forwards;
+       if (fragsize > size)
+               return -1;
+       start = 0;
+       if (line > 1) {
+               unsigned long offset = 0;
+               i = line-1;
+               while (offset + fragsize <= size) {
+                       if (buf[offset++] == '\n') {
+                               start = offset;
+                               if (!--i)
+                                       break;
+                       }
+               }
+       }
+       /* Exact line number? */
+       if (!memcmp(buf + start, fragment, fragsize))
+               return start;
+       /*
+        * There's probably some smart way to do this, but I'll leave
+        * that to the smart and beautiful people. I'm simple and stupid.
+        */
+       backwards = start;
+       forwards = start;
+       for (i = 0; ; i++) {
+               unsigned long try;
+               int n;
+               /* "backward" */
+               if (i & 1) {
+                       if (!backwards) {
+                               if (forwards + fragsize > size)
+                                       break;
+                               continue;
+                       }
+                       do {
+                               --backwards;
+                       } while (backwards && buf[backwards-1] != '\n');
+                       try = backwards;
+               } else {
+                       while (forwards + fragsize <= size) {
+                               if (buf[forwards++] == '\n')
+                                       break;
+                       }
+                       try = forwards;
+               }
+               if (try + fragsize > size)
+                       continue;
+               if (memcmp(buf + try, fragment, fragsize))
+                       continue;
+               n = (i >> 1)+1;
+               if (i & 1)
+                       n = -n;
+               *lines = n;
+               return try;
+       }
+       /*
+        * We should start searching forward and backward.
+        */
+       return -1;
+ }
+ static void remove_first_line(const char **rbuf, int *rsize)
+ {
+       const char *buf = *rbuf;
+       int size = *rsize;
+       unsigned long offset;
+       offset = 0;
+       while (offset <= size) {
+               if (buf[offset++] == '\n')
+                       break;
+       }
+       *rsize = size - offset;
+       *rbuf = buf + offset;
+ }
+ static void remove_last_line(const char **rbuf, int *rsize)
+ {
+       const char *buf = *rbuf;
+       int size = *rsize;
+       unsigned long offset;
+       offset = size - 1;
+       while (offset > 0) {
+               if (buf[--offset] == '\n')
+                       break;
+       }
+       *rsize = offset + 1;
+ }
+ struct buffer_desc {
+       char *buffer;
+       unsigned long size;
+       unsigned long alloc;
+ };
+ static int apply_line(char *output, const char *patch, int plen)
+ {
+       /* plen is number of bytes to be copied from patch,
+        * starting at patch+1 (patch[0] is '+').  Typically
+        * patch[plen] is '\n'.
+        */
+       int add_nl_to_tail = 0;
+       if ((new_whitespace == strip_whitespace) &&
+           1 < plen && isspace(patch[plen-1])) {
+               if (patch[plen] == '\n')
+                       add_nl_to_tail = 1;
+               plen--;
+               while (0 < plen && isspace(patch[plen]))
+                       plen--;
+               applied_after_stripping++;
+       }
+       memcpy(output, patch + 1, plen);
+       if (add_nl_to_tail)
+               output[plen++] = '\n';
+       return plen;
+ }
+ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
+ {
+       char *buf = desc->buffer;
+       const char *patch = frag->patch;
+       int offset, size = frag->size;
+       char *old = xmalloc(size);
+       char *new = xmalloc(size);
+       const char *oldlines, *newlines;
+       int oldsize = 0, newsize = 0;
+       unsigned long leading, trailing;
+       int pos, lines;
+       while (size > 0) {
+               int len = linelen(patch, size);
+               int plen;
+               if (!len)
+                       break;
+               /*
+                * "plen" is how much of the line we should use for
+                * the actual patch data. Normally we just remove the
+                * first character on the line, but if the line is
+                * followed by "\ No newline", then we also remove the
+                * last one (which is the newline, of course).
+                */
+               plen = len-1;
+               if (len < size && patch[len] == '\\')
+                       plen--;
+               switch (*patch) {
+               case ' ':
+               case '-':
+                       memcpy(old + oldsize, patch + 1, plen);
+                       oldsize += plen;
+                       if (*patch == '-')
+                               break;
+               /* Fall-through for ' ' */
+               case '+':
+                       if (*patch != '+' || !no_add)
+                               newsize += apply_line(new + newsize, patch,
+                                                     plen);
+                       break;
+               case '@': case '\\':
+                       /* Ignore it, we already handled it */
+                       break;
+               default:
+                       return -1;
+               }
+               patch += len;
+               size -= len;
+       }
+ #ifdef NO_ACCURATE_DIFF
+       if (oldsize > 0 && old[oldsize - 1] == '\n' &&
+                       newsize > 0 && new[newsize - 1] == '\n') {
+               oldsize--;
+               newsize--;
+       }
+ #endif
+       oldlines = old;
+       newlines = new;
+       leading = frag->leading;
+       trailing = frag->trailing;
+       lines = 0;
+       pos = frag->newpos;
+       for (;;) {
+               offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
+               if (offset >= 0) {
+                       int diff = newsize - oldsize;
+                       unsigned long size = desc->size + diff;
+                       unsigned long alloc = desc->alloc;
+                       /* Warn if it was necessary to reduce the number
+                        * of context lines.
+                        */
+                       if ((leading != frag->leading) || (trailing != frag->trailing))
+                               fprintf(stderr, "Context reduced to (%ld/%ld) to apply fragment at %d\n",
+                                       leading, trailing, pos + lines);
+                       if (size > alloc) {
+                               alloc = size + 8192;
+                               desc->alloc = alloc;
+                               buf = xrealloc(buf, alloc);
+                               desc->buffer = buf;
+                       }
+                       desc->size = size;
+                       memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+                       memcpy(buf + offset, newlines, newsize);
+                       offset = 0;
+                       break;
+               }
+               /* Am I at my context limits? */
+               if ((leading <= p_context) && (trailing <= p_context))
+                       break;
+               /* Reduce the number of context lines
+                * Reduce both leading and trailing if they are equal
+                * otherwise just reduce the larger context.
+                */
+               if (leading >= trailing) {
+                       remove_first_line(&oldlines, &oldsize);
+                       remove_first_line(&newlines, &newsize);
+                       pos--;
+                       leading--;
+               }
+               if (trailing > leading) {
+                       remove_last_line(&oldlines, &oldsize);
+                       remove_last_line(&newlines, &newsize);
+                       trailing--;
+               }
+       }
+       free(old);
+       free(new);
+       return offset;
+ }
+ static char *inflate_it(const void *data, unsigned long size,
+                       unsigned long inflated_size)
+ {
+       z_stream stream;
+       void *out;
+       int st;
+       memset(&stream, 0, sizeof(stream));
+       stream.next_in = (unsigned char *)data;
+       stream.avail_in = size;
+       stream.next_out = out = xmalloc(inflated_size);
+       stream.avail_out = inflated_size;
+       inflateInit(&stream);
+       st = inflate(&stream, Z_FINISH);
+       if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
+               free(out);
+               return NULL;
+       }
+       return out;
+ }
+ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
+ {
+       unsigned long dst_size;
+       struct fragment *fragment = patch->fragments;
+       void *data;
+       void *result;
+       data = inflate_it(fragment->patch, fragment->size,
+                         patch->deflate_origlen);
+       if (!data)
+               return error("corrupt patch data");
+       switch (patch->is_binary) {
+       case BINARY_DELTA_DEFLATED:
+               result = patch_delta(desc->buffer, desc->size,
+                                    data,
+                                    patch->deflate_origlen,
+                                    &dst_size);
+               free(desc->buffer);
+               desc->buffer = result;
+               free(data);
+               break;
+       case BINARY_LITERAL_DEFLATED:
+               free(desc->buffer);
+               desc->buffer = data;
+               dst_size = patch->deflate_origlen;
+               break;
+       }
+       if (!desc->buffer)
+               return -1;
+       desc->size = desc->alloc = dst_size;
+       return 0;
+ }
+ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
+ {
+       const char *name = patch->old_name ? patch->old_name : patch->new_name;
+       unsigned char sha1[20];
+       unsigned char hdr[50];
+       int hdrlen;
+       if (!allow_binary_replacement)
+               return error("cannot apply binary patch to '%s' "
+                            "without --allow-binary-replacement",
+                            name);
+       /* For safety, we require patch index line to contain
+        * full 40-byte textual SHA1 for old and new, at least for now.
+        */
+       if (strlen(patch->old_sha1_prefix) != 40 ||
+           strlen(patch->new_sha1_prefix) != 40 ||
+           get_sha1_hex(patch->old_sha1_prefix, sha1) ||
+           get_sha1_hex(patch->new_sha1_prefix, sha1))
+               return error("cannot apply binary patch to '%s' "
+                            "without full index line", name);
+       if (patch->old_name) {
+               /* See if the old one matches what the patch
+                * applies to.
+                */
+               write_sha1_file_prepare(desc->buffer, desc->size,
+                                       blob_type, sha1, hdr, &hdrlen);
+               if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
+                       return error("the patch applies to '%s' (%s), "
+                                    "which does not match the "
+                                    "current contents.",
+                                    name, sha1_to_hex(sha1));
+       }
+       else {
+               /* Otherwise, the old one must be empty. */
+               if (desc->size)
+                       return error("the patch applies to an empty "
+                                    "'%s' but it is not empty", name);
+       }
+       get_sha1_hex(patch->new_sha1_prefix, sha1);
+       if (!memcmp(sha1, null_sha1, 20)) {
+               free(desc->buffer);
+               desc->alloc = desc->size = 0;
+               desc->buffer = NULL;
+               return 0; /* deletion patch */
+       }
+       if (has_sha1_file(sha1)) {
+               /* We already have the postimage */
+               char type[10];
+               unsigned long size;
+               free(desc->buffer);
+               desc->buffer = read_sha1_file(sha1, type, &size);
+               if (!desc->buffer)
+                       return error("the necessary postimage %s for "
+                                    "'%s' cannot be read",
+                                    patch->new_sha1_prefix, name);
+               desc->alloc = desc->size = size;
+       }
+       else {
+               /* We have verified desc matches the preimage;
+                * apply the patch data to it, which is stored
+                * in the patch->fragments->{patch,size}.
+                */
+               if (apply_binary_fragment(desc, patch))
+                       return error("binary patch does not apply to '%s'",
+                                    name);
+               /* verify that the result matches */
+               write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
+                                       sha1, hdr, &hdrlen);
+               if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
+                       return error("binary patch to '%s' creates incorrect result", name);
+       }
+       return 0;
+ }
+ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+ {
+       struct fragment *frag = patch->fragments;
+       const char *name = patch->old_name ? patch->old_name : patch->new_name;
+       if (patch->is_binary)
+               return apply_binary(desc, patch);
+       while (frag) {
+               if (apply_one_fragment(desc, frag) < 0)
+                       return error("patch failed: %s:%ld",
+                                    name, frag->oldpos);
+               frag = frag->next;
+       }
+       return 0;
+ }
+ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
+ {
+       char *buf;
+       unsigned long size, alloc;
+       struct buffer_desc desc;
+       size = 0;
+       alloc = 0;
+       buf = NULL;
+       if (cached) {
+               if (ce) {
+                       char type[20];
+                       buf = read_sha1_file(ce->sha1, type, &size);
+                       if (!buf)
+                               return error("read of %s failed",
+                                            patch->old_name);
+                       alloc = size;
+               }
+       }
+       else if (patch->old_name) {
+               size = st->st_size;
+               alloc = size + 8192;
+               buf = xmalloc(alloc);
+               if (read_old_data(st, patch->old_name, buf, alloc) != size)
+                       return error("read of %s failed", patch->old_name);
+       }
+       desc.size = size;
+       desc.alloc = alloc;
+       desc.buffer = buf;
+       if (apply_fragments(&desc, patch) < 0)
+               return -1;
+       patch->result = desc.buffer;
+       patch->resultsize = desc.size;
+       if (patch->is_delete && patch->resultsize)
+               return error("removal patch leaves file contents");
+       return 0;
+ }
+ static int check_patch(struct patch *patch)
+ {
+       struct stat st;
+       const char *old_name = patch->old_name;
+       const char *new_name = patch->new_name;
+       const char *name = old_name ? old_name : new_name;
+       struct cache_entry *ce = NULL;
+       if (old_name) {
+               int changed = 0;
+               int stat_ret = 0;
+               unsigned st_mode = 0;
+               if (!cached)
+                       stat_ret = lstat(old_name, &st);
+               if (check_index) {
+                       int pos = cache_name_pos(old_name, strlen(old_name));
+                       if (pos < 0)
+                               return error("%s: does not exist in index",
+                                            old_name);
+                       ce = active_cache[pos];
+                       if (stat_ret < 0) {
+                               struct checkout costate;
+                               if (errno != ENOENT)
+                                       return error("%s: %s", old_name,
+                                                    strerror(errno));
+                               /* checkout */
+                               costate.base_dir = "";
+                               costate.base_dir_len = 0;
+                               costate.force = 0;
+                               costate.quiet = 0;
+                               costate.not_new = 0;
+                               costate.refresh_cache = 1;
+                               if (checkout_entry(ce,
+                                                  &costate,
+                                                  NULL) ||
+                                   lstat(old_name, &st))
+                                       return -1;
+                       }
+                       if (!cached)
+                               changed = ce_match_stat(ce, &st, 1);
+                       if (changed)
+                               return error("%s: does not match index",
+                                            old_name);
+                       if (cached)
+                               st_mode = ntohl(ce->ce_mode);
+               }
+               else if (stat_ret < 0)
+                       return error("%s: %s", old_name, strerror(errno));
+               if (!cached)
+                       st_mode = ntohl(create_ce_mode(st.st_mode));
+               if (patch->is_new < 0)
+                       patch->is_new = 0;
+               if (!patch->old_mode)
+                       patch->old_mode = st_mode;
+               if ((st_mode ^ patch->old_mode) & S_IFMT)
+                       return error("%s: wrong type", old_name);
+               if (st_mode != patch->old_mode)
+                       fprintf(stderr, "warning: %s has type %o, expected %o\n",
+                               old_name, st_mode, patch->old_mode);
+       }
+       if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
+               if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
+                       return error("%s: already exists in index", new_name);
+               if (!cached) {
+                       if (!lstat(new_name, &st))
+                               return error("%s: already exists in working directory", new_name);
+                       if (errno != ENOENT)
+                               return error("%s: %s", new_name, strerror(errno));
+               }
+               if (!patch->new_mode) {
+                       if (patch->is_new)
+                               patch->new_mode = S_IFREG | 0644;
+                       else
+                               patch->new_mode = patch->old_mode;
+               }
+       }
+       if (new_name && old_name) {
+               int same = !strcmp(old_name, new_name);
+               if (!patch->new_mode)
+                       patch->new_mode = patch->old_mode;
+               if ((patch->old_mode ^ patch->new_mode) & S_IFMT)
+                       return error("new mode (%o) of %s does not match old mode (%o)%s%s",
+                               patch->new_mode, new_name, patch->old_mode,
+                               same ? "" : " of ", same ? "" : old_name);
+       }
+       if (apply_data(patch, &st, ce) < 0)
+               return error("%s: patch does not apply", name);
+       return 0;
+ }
+ static int check_patch_list(struct patch *patch)
+ {
+       int error = 0;
+       for (;patch ; patch = patch->next)
+               error |= check_patch(patch);
+       return error;
+ }
+ 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 ",patch->old_mode, sha1_to_hex(sha1_ptr));
+               if (line_termination && quote_c_style(name, NULL, NULL, 0))
+                       quote_c_style(name, NULL, stdout, 0);
+               else
+                       fputs(name, stdout);
+               putchar(line_termination);
+       }
+ }
+ static void stat_patch_list(struct patch *patch)
+ {
+       int files, adds, dels;
+       for (files = adds = dels = 0 ; patch ; patch = patch->next) {
+               files++;
+               adds += patch->lines_added;
+               dels += patch->lines_deleted;
+               show_stats(patch);
+       }
+       printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels);
+ }
+ static void numstat_patch_list(struct patch *patch)
+ {
+       for ( ; patch; patch = patch->next) {
+               const char *name;
+               name = patch->new_name ? patch->new_name : patch->old_name;
+               printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
+               if (line_termination && quote_c_style(name, NULL, NULL, 0))
+                       quote_c_style(name, NULL, stdout, 0);
+               else
+                       fputs(name, stdout);
+               putchar('\n');
+       }
+ }
+ static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
+ {
+       if (mode)
+               printf(" %s mode %06o %s\n", newdelete, mode, name);
+       else
+               printf(" %s %s\n", newdelete, name);
+ }
+ static void show_mode_change(struct patch *p, int show_name)
+ {
+       if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
+               if (show_name)
+                       printf(" mode change %06o => %06o %s\n",
+                              p->old_mode, p->new_mode, p->new_name);
+               else
+                       printf(" mode change %06o => %06o\n",
+                              p->old_mode, p->new_mode);
+       }
+ }
+ static void show_rename_copy(struct patch *p)
+ {
+       const char *renamecopy = p->is_rename ? "rename" : "copy";
+       const char *old, *new;
+       /* Find common prefix */
+       old = p->old_name;
+       new = p->new_name;
+       while (1) {
+               const char *slash_old, *slash_new;
+               slash_old = strchr(old, '/');
+               slash_new = strchr(new, '/');
+               if (!slash_old ||
+                   !slash_new ||
+                   slash_old - old != slash_new - new ||
+                   memcmp(old, new, slash_new - new))
+                       break;
+               old = slash_old + 1;
+               new = slash_new + 1;
+       }
+       /* p->old_name thru old is the common prefix, and old and new
+        * through the end of names are renames
+        */
+       if (old != p->old_name)
+               printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
+                      (int)(old - p->old_name), p->old_name,
+                      old, new, p->score);
+       else
+               printf(" %s %s => %s (%d%%)\n", renamecopy,
+                      p->old_name, p->new_name, p->score);
+       show_mode_change(p, 0);
+ }
+ static void summary_patch_list(struct patch *patch)
+ {
+       struct patch *p;
+       for (p = patch; p; p = p->next) {
+               if (p->is_new)
+                       show_file_mode_name("create", p->new_mode, p->new_name);
+               else if (p->is_delete)
+                       show_file_mode_name("delete", p->old_mode, p->old_name);
+               else {
+                       if (p->is_rename || p->is_copy)
+                               show_rename_copy(p);
+                       else {
+                               if (p->score) {
+                                       printf(" rewrite %s (%d%%)\n",
+                                              p->new_name, p->score);
+                                       show_mode_change(p, 0);
+                               }
+                               else
+                                       show_mode_change(p, 1);
+                       }
+               }
+       }
+ }
+ static void patch_stats(struct patch *patch)
+ {
+       int lines = patch->lines_added + patch->lines_deleted;
+       if (lines > max_change)
+               max_change = lines;
+       if (patch->old_name) {
+               int len = quote_c_style(patch->old_name, NULL, NULL, 0);
+               if (!len)
+                       len = strlen(patch->old_name);
+               if (len > max_len)
+                       max_len = len;
+       }
+       if (patch->new_name) {
+               int len = quote_c_style(patch->new_name, NULL, NULL, 0);
+               if (!len)
+                       len = strlen(patch->new_name);
+               if (len > max_len)
+                       max_len = len;
+       }
+ }
+ static void remove_file(struct patch *patch)
+ {
+       if (write_index) {
+               if (remove_file_from_cache(patch->old_name) < 0)
+                       die("unable to remove %s from index", patch->old_name);
++              cache_tree_invalidate_path(active_cache_tree, patch->old_name);
+       }
+       if (!cached)
+               unlink(patch->old_name);
+ }
+ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
+ {
+       struct stat st;
+       struct cache_entry *ce;
+       int namelen = strlen(path);
+       unsigned ce_size = cache_entry_size(namelen);
+       if (!write_index)
+               return;
+       ce = xcalloc(1, ce_size);
+       memcpy(ce->name, path, namelen);
+       ce->ce_mode = create_ce_mode(mode);
+       ce->ce_flags = htons(namelen);
+       if (!cached) {
+               if (lstat(path, &st) < 0)
+                       die("unable to stat newly created file %s", path);
+               fill_stat_cache_info(ce, &st);
+       }
+       if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
+               die("unable to create backing store for newly created file %s", path);
+       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
+               die("unable to add cache entry for %s", path);
+ }
+ static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
+ {
+       int fd;
+       if (S_ISLNK(mode))
+               return symlink(buf, path);
+       fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
+       if (fd < 0)
+               return -1;
+       while (size) {
+               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;
+               size -= written;
+       }
+       if (close(fd) < 0)
+               die("closing file %s: %s", path, strerror(errno));
+       return 0;
+ }
+ /*
+  * We optimistically assume that the directories exist,
+  * which is true 99% of the time anyway. If they don't,
+  * we create them and try again.
+  */
+ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
+ {
+       if (cached)
+               return;
+       if (!try_create_file(path, mode, buf, size))
+               return;
+       if (errno == ENOENT) {
+               if (safe_create_leading_directories(path))
+                       return;
+               if (!try_create_file(path, mode, buf, size))
+                       return;
+       }
+       if (errno == EEXIST) {
+               unsigned int nr = getpid();
+               for (;;) {
+                       const char *newpath;
+                       newpath = mkpath("%s~%u", path, nr);
+                       if (!try_create_file(newpath, mode, buf, size)) {
+                               if (!rename(newpath, path))
+                                       return;
+                               unlink(newpath);
+                               break;
+                       }
+                       if (errno != EEXIST)
+                               break;
+                       ++nr;
+               }
+       }
+       die("unable to write file %s mode %o", path, mode);
+ }
+ static void create_file(struct patch *patch)
+ {
+       char *path = patch->new_name;
+       unsigned mode = patch->new_mode;
+       unsigned long size = patch->resultsize;
+       char *buf = patch->result;
+       if (!mode)
+               mode = S_IFREG | 0644;
++      create_one_file(path, mode, buf, size);
+       add_index_file(path, mode, buf, size);
++      cache_tree_invalidate_path(active_cache_tree, path);
+ }
+ static void write_out_one_result(struct patch *patch)
+ {
+       if (patch->is_delete > 0) {
+               remove_file(patch);
+               return;
+       }
+       if (patch->is_new > 0 || patch->is_copy) {
+               create_file(patch);
+               return;
+       }
+       /*
+        * Rename or modification boils down to the same
+        * thing: remove the old, write the new
+        */
+       remove_file(patch);
+       create_file(patch);
+ }
+ static void write_out_results(struct patch *list, int skipped_patch)
+ {
+       if (!list && !skipped_patch)
+               die("No changes");
+       while (list) {
+               write_out_one_result(list);
+               list = list->next;
+       }
+ }
+ static struct cache_file cache_file;
+ static struct excludes {
+       struct excludes *next;
+       const char *path;
+ } *excludes;
+ static int use_patch(struct patch *p)
+ {
+       const char *pathname = p->new_name ? p->new_name : p->old_name;
+       struct excludes *x = excludes;
+       while (x) {
+               if (fnmatch(x->path, pathname, 0) == 0)
+                       return 0;
+               x = x->next;
+       }
+       if (0 < prefix_length) {
+               int pathlen = strlen(pathname);
+               if (pathlen <= prefix_length ||
+                   memcmp(prefix, pathname, prefix_length))
+                       return 0;
+       }
+       return 1;
+ }
+ static int apply_patch(int fd, const char *filename)
+ {
+       unsigned long offset, size;
+       char *buffer = read_patch_file(fd, &size);
+       struct patch *list = NULL, **listp = &list;
+       int skipped_patch = 0;
+       patch_input_file = filename;
+       if (!buffer)
+               return -1;
+       offset = 0;
+       while (size > 0) {
+               struct patch *patch;
+               int nr;
+               patch = xcalloc(1, sizeof(*patch));
+               nr = parse_chunk(buffer + offset, size, patch);
+               if (nr < 0)
+                       break;
+               if (use_patch(patch)) {
+                       patch_stats(patch);
+                       *listp = patch;
+                       listp = &patch->next;
+               } else {
+                       /* perhaps free it a bit better? */
+                       free(patch);
+                       skipped_patch++;
+               }
+               offset += nr;
+               size -= nr;
+       }
+       if (whitespace_error && (new_whitespace == error_on_whitespace))
+               apply = 0;
+       write_index = check_index && apply;
+       if (write_index && newfd < 0)
+               newfd = hold_index_file_for_update(&cache_file, get_index_file());
+       if (check_index) {
+               if (read_cache() < 0)
+                       die("unable to read index file");
+       }
+       if ((check || apply) && check_patch_list(list) < 0)
+               exit(1);
+       if (apply)
+               write_out_results(list, skipped_patch);
+       if (show_index_info)
+               show_index_list(list);
+       if (diffstat)
+               stat_patch_list(list);
+       if (numstat)
+               numstat_patch_list(list);
+       if (summary)
+               summary_patch_list(list);
+       free(buffer);
+       return 0;
+ }
+ static int git_apply_config(const char *var, const char *value)
+ {
+       if (!strcmp(var, "apply.whitespace")) {
+               apply_default_whitespace = strdup(value);
+               return 0;
+       }
+       return git_default_config(var, value);
+ }
+ int cmd_apply(int argc, const char **argv, char **envp)
+ {
+       int i;
+       int read_stdin = 1;
+       const char *whitespace_option = NULL;
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               char *end;
+               int fd;
+               if (!strcmp(arg, "-")) {
+                       apply_patch(0, "<stdin>");
+                       read_stdin = 0;
+                       continue;
+               }
+               if (!strncmp(arg, "--exclude=", 10)) {
+                       struct excludes *x = xmalloc(sizeof(*x));
+                       x->path = arg + 10;
+                       x->next = excludes;
+                       excludes = x;
+                       continue;
+               }
+               if (!strncmp(arg, "-p", 2)) {
+                       p_value = atoi(arg + 2);
+                       continue;
+               }
+               if (!strcmp(arg, "--no-add")) {
+                       no_add = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--stat")) {
+                       apply = 0;
+                       diffstat = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--allow-binary-replacement") ||
+                   !strcmp(arg, "--binary")) {
+                       allow_binary_replacement = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--numstat")) {
+                       apply = 0;
+                       numstat = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--summary")) {
+                       apply = 0;
+                       summary = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--check")) {
+                       apply = 0;
+                       check = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--index")) {
+                       check_index = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--cached")) {
+                       check_index = 1;
+                       cached = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--apply")) {
+                       apply = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--index-info")) {
+                       apply = 0;
+                       show_index_info = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-z")) {
+                       line_termination = 0;
+                       continue;
+               }
+               if (!strncmp(arg, "-C", 2)) {
+                       p_context = strtoul(arg + 2, &end, 0);
+                       if (*end != '\0')
+                               die("unrecognized context count '%s'", arg + 2);
+                       continue;
+               }
+               if (!strncmp(arg, "--whitespace=", 13)) {
+                       whitespace_option = arg + 13;
+                       parse_whitespace_option(arg + 13);
+                       continue;
+               }
+               if (check_index && prefix_length < 0) {
+                       prefix = setup_git_directory();
+                       prefix_length = prefix ? strlen(prefix) : 0;
+                       git_config(git_apply_config);
+                       if (!whitespace_option && apply_default_whitespace)
+                               parse_whitespace_option(apply_default_whitespace);
+               }
+               if (0 < prefix_length)
+                       arg = prefix_filename(prefix, prefix_length, arg);
+               fd = open(arg, O_RDONLY);
+               if (fd < 0)
+                       usage(apply_usage);
+               read_stdin = 0;
+               set_default_whitespace_mode(whitespace_option);
+               apply_patch(fd, arg);
+               close(fd);
+       }
+       set_default_whitespace_mode(whitespace_option);
+       if (read_stdin)
+               apply_patch(0, "<stdin>");
+       if (whitespace_error) {
+               if (squelch_whitespace_errors &&
+                   squelch_whitespace_errors < whitespace_error) {
+                       int squelched =
+                               whitespace_error - squelch_whitespace_errors;
+                       fprintf(stderr, "warning: squelched %d whitespace error%s\n",
+                               squelched,
+                               squelched == 1 ? "" : "s");
+               }
+               if (new_whitespace == error_on_whitespace)
+                       die("%d line%s add%s trailing whitespaces.",
+                           whitespace_error,
+                           whitespace_error == 1 ? "" : "s",
+                           whitespace_error == 1 ? "s" : "");
+               if (applied_after_stripping)
+                       fprintf(stderr, "warning: %d line%s applied after"
+                               " stripping trailing whitespaces.\n",
+                               applied_after_stripping,
+                               applied_after_stripping == 1 ? "" : "s");
+               else if (whitespace_error)
+                       fprintf(stderr, "warning: %d line%s add%s trailing"
+                               " whitespaces.\n",
+                               whitespace_error,
+                               whitespace_error == 1 ? "" : "s",
+                               whitespace_error == 1 ? "s" : "");
+       }
+       if (write_index) {
+               if (write_cache(newfd, active_cache, active_nr) ||
+                   commit_index_file(&cache_file))
+                       die("Unable to write new cachefile");
+       }
+       return 0;
+ }
index 0000000,3a0c5f2..8dae9f7
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,824 +1,499 @@@
 -#include <dirent.h>
+ /*
+  * This merges the file listing in the directory cache index
+  * with the actual working directory list, and shows different
+  * combinations of the two.
+  *
+  * Copyright (C) Linus Torvalds, 2005
+  */
 -static int show_ignored = 0;
+ #include <fnmatch.h>
+ #include "cache.h"
+ #include "quote.h"
++#include "dir.h"
+ #include "builtin.h"
+ static int abbrev = 0;
+ static int show_deleted = 0;
+ static int show_cached = 0;
+ static int show_others = 0;
 -static int show_other_directories = 0;
 -static int hide_empty_directories = 0;
+ static int show_stage = 0;
+ static int show_unmerged = 0;
+ static int show_modified = 0;
+ static int show_killed = 0;
 -static const char *exclude_per_dir = NULL;
 -
 -/* We maintain three exclude pattern lists:
 - * EXC_CMDL lists patterns explicitly given on the command line.
 - * EXC_DIRS lists patterns obtained from per-directory ignore files.
 - * EXC_FILE lists patterns from fallback ignore files.
 - */
 -#define EXC_CMDL 0
 -#define EXC_DIRS 1
 -#define EXC_FILE 2
 -static struct exclude_list {
 -      int nr;
 -      int alloc;
 -      struct exclude {
 -              const char *pattern;
 -              const char *base;
 -              int baselen;
 -      } **excludes;
 -} exclude_list[3];
 -
 -static void add_exclude(const char *string, const char *base,
 -                      int baselen, struct exclude_list *which)
 -{
 -      struct exclude *x = xmalloc(sizeof (*x));
 -
 -      x->pattern = string;
 -      x->base = base;
 -      x->baselen = baselen;
 -      if (which->nr == which->alloc) {
 -              which->alloc = alloc_nr(which->alloc);
 -              which->excludes = realloc(which->excludes,
 -                                        which->alloc * sizeof(x));
 -      }
 -      which->excludes[which->nr++] = x;
 -}
 -
 -static int add_excludes_from_file_1(const char *fname,
 -                                  const char *base,
 -                                  int baselen,
 -                                  struct exclude_list *which)
 -{
 -      int fd, i;
 -      long size;
 -      char *buf, *entry;
 -
 -      fd = open(fname, O_RDONLY);
 -      if (fd < 0)
 -              goto err;
 -      size = lseek(fd, 0, SEEK_END);
 -      if (size < 0)
 -              goto err;
 -      lseek(fd, 0, SEEK_SET);
 -      if (size == 0) {
 -              close(fd);
 -              return 0;
 -      }
 -      buf = xmalloc(size+1);
 -      if (read(fd, buf, size) != size)
 -              goto err;
 -      close(fd);
 -
 -      buf[size++] = '\n';
 -      entry = buf;
 -      for (i = 0; i < size; i++) {
 -              if (buf[i] == '\n') {
 -                      if (entry != buf + i && entry[0] != '#') {
 -                              buf[i - (i && buf[i-1] == '\r')] = 0;
 -                              add_exclude(entry, base, baselen, which);
 -                      }
 -                      entry = buf + i + 1;
 -              }
 -      }
 -      return 0;
 -
 - err:
 -      if (0 <= fd)
 -              close(fd);
 -      return -1;
 -}
 -
 -static void add_excludes_from_file(const char *fname)
 -{
 -      if (add_excludes_from_file_1(fname, "", 0,
 -                                   &exclude_list[EXC_FILE]) < 0)
 -              die("cannot use %s as an exclude file", fname);
 -}
 -
 -static int push_exclude_per_directory(const char *base, int baselen)
 -{
 -      char exclude_file[PATH_MAX];
 -      struct exclude_list *el = &exclude_list[EXC_DIRS];
 -      int current_nr = el->nr;
 -
 -      if (exclude_per_dir) {
 -              memcpy(exclude_file, base, baselen);
 -              strcpy(exclude_file + baselen, exclude_per_dir);
 -              add_excludes_from_file_1(exclude_file, base, baselen, el);
 -      }
 -      return current_nr;
 -}
 -
 -static void pop_exclude_per_directory(int stk)
 -{
 -      struct exclude_list *el = &exclude_list[EXC_DIRS];
 -
 -      while (stk < el->nr)
 -              free(el->excludes[--el->nr]);
 -}
 -
 -/* Scan the list and let the last match determines the fate.
 - * Return 1 for exclude, 0 for include and -1 for undecided.
 - */
 -static int excluded_1(const char *pathname,
 -                    int pathlen,
 -                    struct exclude_list *el)
 -{
 -      int i;
 -
 -      if (el->nr) {
 -              for (i = el->nr - 1; 0 <= i; i--) {
 -                      struct exclude *x = el->excludes[i];
 -                      const char *exclude = x->pattern;
 -                      int to_exclude = 1;
 -
 -                      if (*exclude == '!') {
 -                              to_exclude = 0;
 -                              exclude++;
 -                      }
 -
 -                      if (!strchr(exclude, '/')) {
 -                              /* match basename */
 -                              const char *basename = strrchr(pathname, '/');
 -                              basename = (basename) ? basename+1 : pathname;
 -                              if (fnmatch(exclude, basename, 0) == 0)
 -                                      return to_exclude;
 -                      }
 -                      else {
 -                              /* match with FNM_PATHNAME:
 -                               * exclude has base (baselen long) implicitly
 -                               * in front of it.
 -                               */
 -                              int baselen = x->baselen;
 -                              if (*exclude == '/')
 -                                      exclude++;
 -
 -                              if (pathlen < baselen ||
 -                                  (baselen && pathname[baselen-1] != '/') ||
 -                                  strncmp(pathname, x->base, baselen))
 -                                  continue;
 -
 -                              if (fnmatch(exclude, pathname+baselen,
 -                                          FNM_PATHNAME) == 0)
 -                                      return to_exclude;
 -                      }
 -              }
 -      }
 -      return -1; /* undecided */
 -}
 -
 -static int excluded(const char *pathname)
 -{
 -      int pathlen = strlen(pathname);
 -      int st;
 -
 -      for (st = EXC_CMDL; st <= EXC_FILE; st++) {
 -              switch (excluded_1(pathname, pathlen, &exclude_list[st])) {
 -              case 0:
 -                      return 0;
 -              case 1:
 -                      return 1;
 -              }
 -      }
 -      return 0;
 -}
 -
 -struct nond_on_fs {
 -      int len;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -static struct nond_on_fs **dir;
 -static int nr_dir;
 -static int dir_alloc;
 -
 -static void add_name(const char *pathname, int len)
 -{
 -      struct nond_on_fs *ent;
 -
 -      if (cache_name_pos(pathname, len) >= 0)
 -              return;
 -
 -      if (nr_dir == dir_alloc) {
 -              dir_alloc = alloc_nr(dir_alloc);
 -              dir = xrealloc(dir, dir_alloc*sizeof(ent));
 -      }
 -      ent = xmalloc(sizeof(*ent) + len + 1);
 -      ent->len = len;
 -      memcpy(ent->name, pathname, len);
 -      ent->name[len] = 0;
 -      dir[nr_dir++] = ent;
 -}
 -
 -static int dir_exists(const char *dirname, int len)
 -{
 -      int pos = cache_name_pos(dirname, len);
 -      if (pos >= 0)
 -              return 1;
 -      pos = -pos-1;
 -      if (pos >= active_nr) /* can't */
 -              return 0;
 -      return !strncmp(active_cache[pos]->name, dirname, len);
 -}
 -
 -/*
 - * Read a directory tree. We currently ignore anything but
 - * directories, regular files and symlinks. That's because git
 - * doesn't handle them at all yet. Maybe that will change some
 - * day.
 - *
 - * Also, we ignore the name ".git" (even if it is not a directory).
 - * That likely will not change.
 - */
 -static int read_directory(const char *path, const char *base, int baselen)
 -{
 -      DIR *fdir = opendir(path);
 -      int contents = 0;
 -
 -      if (fdir) {
 -              int exclude_stk;
 -              struct dirent *de;
 -              char fullname[MAXPATHLEN + 1];
 -              memcpy(fullname, base, baselen);
 -
 -              exclude_stk = push_exclude_per_directory(base, baselen);
 -
 -              while ((de = readdir(fdir)) != NULL) {
 -                      int len;
 -
 -                      if ((de->d_name[0] == '.') &&
 -                          (de->d_name[1] == 0 ||
 -                           !strcmp(de->d_name + 1, ".") ||
 -                           !strcmp(de->d_name + 1, "git")))
 -                              continue;
 -                      len = strlen(de->d_name);
 -                      memcpy(fullname + baselen, de->d_name, len+1);
 -                      if (excluded(fullname) != show_ignored) {
 -                              if (!show_ignored || DTYPE(de) != DT_DIR) {
 -                                      continue;
 -                              }
 -                      }
 -
 -                      switch (DTYPE(de)) {
 -                      struct stat st;
 -                      int subdir, rewind_base;
 -                      default:
 -                              continue;
 -                      case DT_UNKNOWN:
 -                              if (lstat(fullname, &st))
 -                                      continue;
 -                              if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
 -                                      break;
 -                              if (!S_ISDIR(st.st_mode))
 -                                      continue;
 -                              /* fallthrough */
 -                      case DT_DIR:
 -                              memcpy(fullname + baselen + len, "/", 2);
 -                              len++;
 -                              rewind_base = nr_dir;
 -                              subdir = read_directory(fullname, fullname,
 -                                                      baselen + len);
 -                              if (show_other_directories &&
 -                                  (subdir || !hide_empty_directories) &&
 -                                  !dir_exists(fullname, baselen + len)) {
 -                                      // Rewind the read subdirectory
 -                                      while (nr_dir > rewind_base)
 -                                              free(dir[--nr_dir]);
 -                                      break;
 -                              }
 -                              contents += subdir;
 -                              continue;
 -                      case DT_REG:
 -                      case DT_LNK:
 -                              break;
 -                      }
 -                      add_name(fullname, baselen + len);
 -                      contents++;
 -              }
 -              closedir(fdir);
 -
 -              pop_exclude_per_directory(exclude_stk);
 -      }
 -
 -      return contents;
 -}
 -
 -static int cmp_name(const void *p1, const void *p2)
 -{
 -      const struct nond_on_fs *e1 = *(const struct nond_on_fs **)p1;
 -      const struct nond_on_fs *e2 = *(const struct nond_on_fs **)p2;
 -
 -      return cache_name_compare(e1->name, e1->len,
 -                                e2->name, e2->len);
 -}
+ static int show_valid_bit = 0;
+ static int line_terminator = '\n';
+ static int prefix_len = 0, prefix_offset = 0;
+ static const char *prefix = NULL;
+ static const char **pathspec = NULL;
+ static int error_unmatch = 0;
+ static char *ps_matched = NULL;
+ static const char *tag_cached = "";
+ static const char *tag_unmerged = "";
+ static const char *tag_removed = "";
+ static const char *tag_other = "";
+ static const char *tag_killed = "";
+ static const char *tag_modified = "";
 -static void show_dir_entry(const char *tag, struct nond_on_fs *ent)
+ /*
+  * Match a pathspec against a filename. The first "len" characters
+  * are the common prefix
+  */
+ static int match(const char **spec, char *ps_matched,
+                const char *filename, int len)
+ {
+       const char *m;
+       while ((m = *spec++) != NULL) {
+               int matchlen = strlen(m + len);
+               if (!matchlen)
+                       goto matched;
+               if (!strncmp(m + len, filename + len, matchlen)) {
+                       if (m[len + matchlen - 1] == '/')
+                               goto matched;
+                       switch (filename[len + matchlen]) {
+                       case '/': case '\0':
+                               goto matched;
+                       }
+               }
+               if (!fnmatch(m + len, filename + len, 0))
+                       goto matched;
+               if (ps_matched)
+                       ps_matched++;
+               continue;
+       matched:
+               if (ps_matched)
+                       *ps_matched = 1;
+               return 1;
+       }
+       return 0;
+ }
 -static void show_other_files(void)
++static void show_dir_entry(const char *tag, struct dir_entry *ent)
+ {
+       int len = prefix_len;
+       int offset = prefix_offset;
+       if (len >= ent->len)
+               die("git-ls-files: internal error - directory entry not superset of prefix");
+       if (pathspec && !match(pathspec, ps_matched, ent->name, len))
+               return;
+       fputs(tag, stdout);
+       write_name_quoted("", 0, ent->name + offset, line_terminator, stdout);
+       putchar(line_terminator);
+ }
 -      for (i = 0; i < nr_dir; i++) {
++static void show_other_files(struct dir_struct *dir)
+ {
+       int i;
 -              struct nond_on_fs *ent = dir[i];
++      for (i = 0; i < dir->nr; i++) {
+               /* We should not have a matching entry, but we
+                * may have an unmerged entry for this path.
+                */
 -static void show_killed_files(void)
++              struct dir_entry *ent = dir->entries[i];
+               int pos = cache_name_pos(ent->name, ent->len);
+               struct cache_entry *ce;
+               if (0 <= pos)
+                       die("bug in show-other-files");
+               pos = -pos - 1;
+               if (pos < active_nr) { 
+                       ce = active_cache[pos];
+                       if (ce_namelen(ce) == ent->len &&
+                           !memcmp(ce->name, ent->name, ent->len))
+                               continue; /* Yup, this one exists unmerged */
+               }
+               show_dir_entry(tag_other, ent);
+       }
+ }
 -      for (i = 0; i < nr_dir; i++) {
 -              struct nond_on_fs *ent = dir[i];
++static void show_killed_files(struct dir_struct *dir)
+ {
+       int i;
 -                      show_dir_entry(tag_killed, dir[i]);
++      for (i = 0; i < dir->nr; i++) {
++              struct dir_entry *ent = dir->entries[i];
+               char *cp, *sp;
+               int pos, len, killed = 0;
+               for (cp = ent->name; cp - ent->name < ent->len; cp = sp + 1) {
+                       sp = strchr(cp, '/');
+                       if (!sp) {
+                               /* If ent->name is prefix of an entry in the
+                                * cache, it will be killed.
+                                */
+                               pos = cache_name_pos(ent->name, ent->len);
+                               if (0 <= pos)
+                                       die("bug in show-killed-files");
+                               pos = -pos - 1;
+                               while (pos < active_nr &&
+                                      ce_stage(active_cache[pos]))
+                                       pos++; /* skip unmerged */
+                               if (active_nr <= pos)
+                                       break;
+                               /* pos points at a name immediately after
+                                * ent->name in the cache.  Does it expect
+                                * ent->name to be a directory?
+                                */
+                               len = ce_namelen(active_cache[pos]);
+                               if ((ent->len < len) &&
+                                   !strncmp(active_cache[pos]->name,
+                                            ent->name, ent->len) &&
+                                   active_cache[pos]->name[ent->len] == '/')
+                                       killed = 1;
+                               break;
+                       }
+                       if (0 <= cache_name_pos(ent->name, sp - ent->name)) {
+                               /* If any of the leading directories in
+                                * ent->name is registered in the cache,
+                                * ent->name will be killed.
+                                */
+                               killed = 1;
+                               break;
+                       }
+               }
+               if (killed)
 -static void show_files(void)
++                      show_dir_entry(tag_killed, dir->entries[i]);
+       }
+ }
+ static void show_ce_entry(const char *tag, struct cache_entry *ce)
+ {
+       int len = prefix_len;
+       int offset = prefix_offset;
+       if (len >= ce_namelen(ce))
+               die("git-ls-files: internal error - cache entry not superset of prefix");
+       if (pathspec && !match(pathspec, ps_matched, ce->name, len))
+               return;
+       if (tag && *tag && show_valid_bit &&
+           (ce->ce_flags & htons(CE_VALID))) {
+               static char alttag[4];
+               memcpy(alttag, tag, 3);
+               if (isalpha(tag[0]))
+                       alttag[0] = tolower(tag[0]);
+               else if (tag[0] == '?')
+                       alttag[0] = '!';
+               else {
+                       alttag[0] = 'v';
+                       alttag[1] = tag[0];
+                       alttag[2] = ' ';
+                       alttag[3] = 0;
+               }
+               tag = alttag;
+       }
+       if (!show_stage) {
+               fputs(tag, stdout);
+               write_name_quoted("", 0, ce->name + offset,
+                                 line_terminator, stdout);
+               putchar(line_terminator);
+       }
+       else {
+               printf("%s%06o %s %d\t",
+                      tag,
+                      ntohl(ce->ce_mode),
+                      abbrev ? find_unique_abbrev(ce->sha1,abbrev)
+                               : sha1_to_hex(ce->sha1),
+                      ce_stage(ce));
+               write_name_quoted("", 0, ce->name + offset,
+                                 line_terminator, stdout);
+               putchar(line_terminator);
+       }
+ }
 -              if (baselen) {
++static void show_files(struct dir_struct *dir)
+ {
+       int i;
+       /* For cached/deleted files we don't need to even do the readdir */
+       if (show_others || show_killed) {
+               const char *path = ".", *base = "";
+               int baselen = prefix_len;
 -                      if (exclude_per_dir) {
 -                              char *p, *pp = xmalloc(baselen+1);
 -                              memcpy(pp, prefix, baselen+1);
 -                              p = pp;
 -                              while (1) {
 -                                      char save = *p;
 -                                      *p = 0;
 -                                      push_exclude_per_directory(pp, p-pp);
 -                                      *p++ = save;
 -                                      if (!save)
 -                                              break;
 -                                      p = strchr(p, '/');
 -                                      if (p)
 -                                              p++;
 -                                      else
 -                                              p = pp + baselen;
 -                              }
 -                              free(pp);
 -                      }
 -              }
 -              read_directory(path, base, baselen);
 -              qsort(dir, nr_dir, sizeof(struct nond_on_fs *), cmp_name);
++              if (baselen)
+                       path = base = prefix;
 -                      show_other_files();
++              read_directory(dir, path, base, baselen);
+               if (show_others)
 -                      show_killed_files();
++                      show_other_files(dir);
+               if (show_killed)
 -                      if (excluded(ce->name) != show_ignored)
++                      show_killed_files(dir);
+       }
+       if (show_cached | show_stage) {
+               for (i = 0; i < active_nr; i++) {
+                       struct cache_entry *ce = active_cache[i];
 -                      if (excluded(ce->name) != show_ignored)
++                      if (excluded(dir, ce->name) != dir->show_ignored)
+                               continue;
+                       if (show_unmerged && !ce_stage(ce))
+                               continue;
+                       show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
+               }
+       }
+       if (show_deleted | show_modified) {
+               for (i = 0; i < active_nr; i++) {
+                       struct cache_entry *ce = active_cache[i];
+                       struct stat st;
+                       int err;
 -                      show_ignored = 1;
++                      if (excluded(dir, ce->name) != dir->show_ignored)
+                               continue;
+                       err = lstat(ce->name, &st);
+                       if (show_deleted && err)
+                               show_ce_entry(tag_removed, ce);
+                       if (show_modified && ce_modified(ce, &st, 0))
+                               show_ce_entry(tag_modified, ce);
+               }
+       }
+ }
+ /*
+  * Prune the index to only contain stuff starting with "prefix"
+  */
+ static void prune_cache(void)
+ {
+       int pos = cache_name_pos(prefix, prefix_len);
+       unsigned int first, last;
+       if (pos < 0)
+               pos = -pos-1;
+       active_cache += pos;
+       active_nr -= pos;
+       first = 0;
+       last = active_nr;
+       while (last > first) {
+               int next = (last + first) >> 1;
+               struct cache_entry *ce = active_cache[next];
+               if (!strncmp(ce->name, prefix, prefix_len)) {
+                       first = next+1;
+                       continue;
+               }
+               last = next;
+       }
+       active_nr = last;
+ }
+ static void verify_pathspec(void)
+ {
+       const char **p, *n, *prev;
+       char *real_prefix;
+       unsigned long max;
+       prev = NULL;
+       max = PATH_MAX;
+       for (p = pathspec; (n = *p) != NULL; p++) {
+               int i, len = 0;
+               for (i = 0; i < max; i++) {
+                       char c = n[i];
+                       if (prev && prev[i] != c)
+                               break;
+                       if (!c || c == '*' || c == '?')
+                               break;
+                       if (c == '/')
+                               len = i+1;
+               }
+               prev = n;
+               if (len < max) {
+                       max = len;
+                       if (!max)
+                               break;
+               }
+       }
+       if (prefix_offset > max || memcmp(prev, prefix, prefix_offset))
+               die("git-ls-files: cannot generate relative filenames containing '..'");
+       real_prefix = NULL;
+       prefix_len = max;
+       if (max) {
+               real_prefix = xmalloc(max + 1);
+               memcpy(real_prefix, prev, max);
+               real_prefix[max] = 0;
+       }
+       prefix = real_prefix;
+ }
+ static const char ls_files_usage[] =
+       "git-ls-files [-z] [-t] [-v] (--[cached|deleted|others|stage|unmerged|killed|modified])* "
+       "[ --ignored ] [--exclude=<pattern>] [--exclude-from=<file>] "
+       "[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
+       "[--] [<file>]*";
+ int cmd_ls_files(int argc, const char **argv, char** envp)
+ {
+       int i;
+       int exc_given = 0;
++      struct dir_struct dir;
++      memset(&dir, 0, sizeof(dir));
+       prefix = setup_git_directory();
+       if (prefix)
+               prefix_offset = strlen(prefix);
+       git_config(git_default_config);
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--")) {
+                       i++;
+                       break;
+               }
+               if (!strcmp(arg, "-z")) {
+                       line_terminator = 0;
+                       continue;
+               }
+               if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
+                       tag_cached = "H ";
+                       tag_unmerged = "M ";
+                       tag_removed = "R ";
+                       tag_modified = "C ";
+                       tag_other = "? ";
+                       tag_killed = "K ";
+                       if (arg[1] == 'v')
+                               show_valid_bit = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
+                       show_cached = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
+                       show_deleted = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
+                       show_modified = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
+                       show_others = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
 -                      show_other_directories = 1;
++                      dir.show_ignored = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
+                       show_stage = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
+                       show_killed = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--directory")) {
 -                      hide_empty_directories = 1;
++                      dir.show_other_directories = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--no-empty-directory")) {
 -                      add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]);
++                      dir.hide_empty_directories = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
+                       /* There's no point in showing unmerged unless
+                        * you also show the stage information.
+                        */
+                       show_stage = 1;
+                       show_unmerged = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-x") && i+1 < argc) {
+                       exc_given = 1;
 -                      add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]);
++                      add_exclude(argv[++i], "", 0, &dir.exclude_list[EXC_CMDL]);
+                       continue;
+               }
+               if (!strncmp(arg, "--exclude=", 10)) {
+                       exc_given = 1;
 -                      add_excludes_from_file(argv[++i]);
++                      add_exclude(arg+10, "", 0, &dir.exclude_list[EXC_CMDL]);
+                       continue;
+               }
+               if (!strcmp(arg, "-X") && i+1 < argc) {
+                       exc_given = 1;
 -                      add_excludes_from_file(arg+15);
++                      add_excludes_from_file(&dir, argv[++i]);
+                       continue;
+               }
+               if (!strncmp(arg, "--exclude-from=", 15)) {
+                       exc_given = 1;
 -                      exclude_per_dir = arg + 24;
++                      add_excludes_from_file(&dir, arg+15);
+                       continue;
+               }
+               if (!strncmp(arg, "--exclude-per-directory=", 24)) {
+                       exc_given = 1;
 -      if (show_ignored && !exc_given) {
++                      dir.exclude_per_dir = arg + 24;
+                       continue;
+               }
+               if (!strcmp(arg, "--full-name")) {
+                       prefix_offset = 0;
+                       continue;
+               }
+               if (!strcmp(arg, "--error-unmatch")) {
+                       error_unmatch = 1;
+                       continue;
+               }
+               if (!strncmp(arg, "--abbrev=", 9)) {
+                       abbrev = strtoul(arg+9, NULL, 10);
+                       if (abbrev && abbrev < MINIMUM_ABBREV)
+                               abbrev = MINIMUM_ABBREV;
+                       else if (abbrev > 40)
+                               abbrev = 40;
+                       continue;
+               }
+               if (!strcmp(arg, "--abbrev")) {
+                       abbrev = DEFAULT_ABBREV;
+                       continue;
+               }
+               if (*arg == '-')
+                       usage(ls_files_usage);
+               break;
+       }
+       pathspec = get_pathspec(prefix, argv + i);
+       /* Verify that the pathspec matches the prefix */
+       if (pathspec)
+               verify_pathspec();
+       /* Treat unmatching pathspec elements as errors */
+       if (pathspec && error_unmatch) {
+               int num;
+               for (num = 0; pathspec[num]; num++)
+                       ;
+               ps_matched = xcalloc(1, num);
+       }
 -      show_files();
++      if (dir.show_ignored && !exc_given) {
+               fprintf(stderr, "%s: --ignored needs some exclude pattern\n",
+                       argv[0]);
+               exit(1);
+       }
+       /* With no flags, we default to showing the cached files */
+       if (!(show_stage | show_deleted | show_others | show_unmerged |
+             show_killed | show_modified))
+               show_cached = 1;
+       read_cache();
+       if (prefix)
+               prune_cache();
++      show_files(&dir);
+       if (ps_matched) {
+               /* We need to make sure all pathspec matched otherwise
+                * it is an error.
+                */
+               int num, errors = 0;
+               for (num = 0; pathspec[num]; num++) {
+                       if (ps_matched[num])
+                               continue;
+                       error("pathspec '%s' did not match any.",
+                             pathspec[num] + prefix_offset);
+                       errors++;
+               }
+               return errors ? 1 : 0;
+       }
+       return 0;
+ }
index 0000000,ec40d01..629d151
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,882 +1,998 @@@
 -              if (unpack_trees_rec(posns, len, "", fn, &indpos))
+ /*
+  * GIT - The information manager from hell
+  *
+  * Copyright (C) Linus Torvalds, 2005
+  */
+ #define DBRT_DEBUG 1
+ #include "cache.h"
+ #include "object.h"
+ #include "tree.h"
++#include "cache-tree.h"
+ #include <sys/time.h>
+ #include <signal.h>
+ #include "builtin.h"
+ static int reset = 0;
+ static int merge = 0;
+ static int update = 0;
+ static int index_only = 0;
+ static int nontrivial_merge = 0;
+ static int trivial_merges_only = 0;
+ static int aggressive = 0;
+ static int verbose_update = 0;
+ static volatile int progress_update = 0;
++static const char *prefix = NULL;
+ static int head_idx = -1;
+ static int merge_size = 0;
+ static struct object_list *trees = NULL;
+ static struct cache_entry df_conflict_entry = { 
+ };
+ static struct tree_entry_list df_conflict_list = {
+       .name = NULL,
+       .next = &df_conflict_list
+ };
+ typedef int (*merge_fn_t)(struct cache_entry **src);
+ static int entcmp(char *name1, int dir1, char *name2, int dir2)
+ {
+       int len1 = strlen(name1);
+       int len2 = strlen(name2);
+       int len = len1 < len2 ? len1 : len2;
+       int ret = memcmp(name1, name2, len);
+       unsigned char c1, c2;
+       if (ret)
+               return ret;
+       c1 = name1[len];
+       c2 = name2[len];
+       if (!c1 && dir1)
+               c1 = '/';
+       if (!c2 && dir2)
+               c2 = '/';
+       ret = (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
+       if (c1 && c2 && !ret)
+               ret = len1 - len2;
+       return ret;
+ }
+ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
+                           const char *base, merge_fn_t fn, int *indpos)
+ {
+       int baselen = strlen(base);
+       int src_size = len + 1;
+       do {
+               int i;
+               char *first;
+               int firstdir = 0;
+               int pathlen;
+               unsigned ce_size;
+               struct tree_entry_list **subposns;
+               struct cache_entry **src;
+               int any_files = 0;
+               int any_dirs = 0;
+               char *cache_name;
+               int ce_stage;
+               /* Find the first name in the input. */
+               first = NULL;
+               cache_name = NULL;
+               /* Check the cache */
+               if (merge && *indpos < active_nr) {
+                       /* This is a bit tricky: */
+                       /* If the index has a subdirectory (with
+                        * contents) as the first name, it'll get a
+                        * filename like "foo/bar". But that's after
+                        * "foo", so the entry in trees will get
+                        * handled first, at which point we'll go into
+                        * "foo", and deal with "bar" from the index,
+                        * because the base will be "foo/". The only
+                        * way we can actually have "foo/bar" first of
+                        * all the things is if the trees don't
+                        * contain "foo" at all, in which case we'll
+                        * handle "foo/bar" without going into the
+                        * directory, but that's fine (and will return
+                        * an error anyway, with the added unknown
+                        * file case.
+                        */
+                       cache_name = active_cache[*indpos]->name;
+                       if (strlen(cache_name) > baselen &&
+                           !memcmp(cache_name, base, baselen)) {
+                               cache_name += baselen;
+                               first = cache_name;
+                       } else {
+                               cache_name = NULL;
+                       }
+               }
+ #if DBRT_DEBUG > 1
+               if (first)
+                       printf("index %s\n", first);
+ #endif
+               for (i = 0; i < len; i++) {
+                       if (!posns[i] || posns[i] == &df_conflict_list)
+                               continue;
+ #if DBRT_DEBUG > 1
+                       printf("%d %s\n", i + 1, posns[i]->name);
+ #endif
+                       if (!first || entcmp(first, firstdir,
+                                            posns[i]->name, 
+                                            posns[i]->directory) > 0) {
+                               first = posns[i]->name;
+                               firstdir = posns[i]->directory;
+                       }
+               }
+               /* No name means we're done */
+               if (!first)
+                       return 0;
+               pathlen = strlen(first);
+               ce_size = cache_entry_size(baselen + pathlen);
+               src = xcalloc(src_size, sizeof(struct cache_entry *));
+               subposns = xcalloc(len, sizeof(struct tree_list_entry *));
+               if (cache_name && !strcmp(cache_name, first)) {
+                       any_files = 1;
+                       src[0] = active_cache[*indpos];
+                       remove_cache_entry_at(*indpos);
+               }
+               for (i = 0; i < len; i++) {
+                       struct cache_entry *ce;
+                       if (!posns[i] ||
+                           (posns[i] != &df_conflict_list &&
+                            strcmp(first, posns[i]->name))) {
+                               continue;
+                       }
+                       if (posns[i] == &df_conflict_list) {
+                               src[i + merge] = &df_conflict_entry;
+                               continue;
+                       }
+                       if (posns[i]->directory) {
+                               any_dirs = 1;
+                               parse_tree(posns[i]->item.tree);
+                               subposns[i] = posns[i]->item.tree->entries;
+                               posns[i] = posns[i]->next;
+                               src[i + merge] = &df_conflict_entry;
+                               continue;
+                       }
+                       if (!merge)
+                               ce_stage = 0;
+                       else if (i + 1 < head_idx)
+                               ce_stage = 1;
+                       else if (i + 1 > head_idx)
+                               ce_stage = 3;
+                       else
+                               ce_stage = 2;
+                       ce = xcalloc(1, ce_size);
+                       ce->ce_mode = create_ce_mode(posns[i]->mode);
+                       ce->ce_flags = create_ce_flags(baselen + pathlen,
+                                                      ce_stage);
+                       memcpy(ce->name, base, baselen);
+                       memcpy(ce->name + baselen, first, pathlen + 1);
+                       any_files = 1;
+                       memcpy(ce->sha1, posns[i]->item.any->sha1, 20);
+                       src[i + merge] = ce;
+                       subposns[i] = &df_conflict_list;
+                       posns[i] = posns[i]->next;
+               }
+               if (any_files) {
+                       if (merge) {
+                               int ret;
+ #if DBRT_DEBUG > 1
+                               printf("%s:\n", first);
+                               for (i = 0; i < src_size; i++) {
+                                       printf(" %d ", i);
+                                       if (src[i])
+                                               printf("%s\n", sha1_to_hex(src[i]->sha1));
+                                       else
+                                               printf("\n");
+                               }
+ #endif
+                               ret = fn(src);
+                               
+ #if DBRT_DEBUG > 1
+                               printf("Added %d entries\n", ret);
+ #endif
+                               *indpos += ret;
+                       } else {
+                               for (i = 0; i < src_size; i++) {
+                                       if (src[i]) {
+                                               add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+                                       }
+                               }
+                       }
+               }
+               if (any_dirs) {
+                       char *newbase = xmalloc(baselen + 2 + pathlen);
+                       memcpy(newbase, base, baselen);
+                       memcpy(newbase + baselen, first, pathlen);
+                       newbase[baselen + pathlen] = '/';
+                       newbase[baselen + pathlen + 1] = '\0';
+                       if (unpack_trees_rec(subposns, len, newbase, fn,
+                                            indpos))
+                               return -1;
+                       free(newbase);
+               }
+               free(subposns);
+               free(src);
+       } while (1);
+ }
+ static void reject_merge(struct cache_entry *ce)
+ {
+       die("Entry '%s' would be overwritten by merge. Cannot merge.", 
+           ce->name);
+ }
+ /* Unlink the last component and attempt to remove leading
+  * directories, in case this unlink is the removal of the
+  * last entry in the directory -- empty directories are removed.
+  */
+ static void unlink_entry(char *name)
+ {
+       char *cp, *prev;
+       if (unlink(name))
+               return;
+       prev = NULL;
+       while (1) {
+               int status;
+               cp = strrchr(name, '/');
+               if (prev)
+                       *prev = '/';
+               if (!cp)
+                       break;
+               *cp = 0;
+               status = rmdir(name);
+               if (status) {
+                       *cp = '/';
+                       break;
+               }
+               prev = cp;
+       }
+ }
+ static void progress_interval(int signum)
+ {
+       progress_update = 1;
+ }
+ static void setup_progress_signal(void)
+ {
+       struct sigaction sa;
+       struct itimerval v;
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = progress_interval;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_RESTART;
+       sigaction(SIGALRM, &sa, NULL);
+       v.it_interval.tv_sec = 1;
+       v.it_interval.tv_usec = 0;
+       v.it_value = v.it_interval;
+       setitimer(ITIMER_REAL, &v, NULL);
+ }
+ static void check_updates(struct cache_entry **src, int nr)
+ {
+       static struct checkout state = {
+               .base_dir = "",
+               .force = 1,
+               .quiet = 1,
+               .refresh_cache = 1,
+       };
+       unsigned short mask = htons(CE_UPDATE);
+       unsigned last_percent = 200, cnt = 0, total = 0;
+       if (update && verbose_update) {
+               for (total = cnt = 0; cnt < nr; cnt++) {
+                       struct cache_entry *ce = src[cnt];
+                       if (!ce->ce_mode || ce->ce_flags & mask)
+                               total++;
+               }
+               /* Don't bother doing this for very small updates */
+               if (total < 250)
+                       total = 0;
+               if (total) {
+                       fprintf(stderr, "Checking files out...\n");
+                       setup_progress_signal();
+                       progress_update = 1;
+               }
+               cnt = 0;
+       }
+       while (nr--) {
+               struct cache_entry *ce = *src++;
+               if (total) {
+                       if (!ce->ce_mode || ce->ce_flags & mask) {
+                               unsigned percent;
+                               cnt++;
+                               percent = (cnt * 100) / total;
+                               if (percent != last_percent ||
+                                   progress_update) {
+                                       fprintf(stderr, "%4u%% (%u/%u) done\r",
+                                               percent, cnt, total);
+                                       last_percent = percent;
+                               }
+                       }
+               }
+               if (!ce->ce_mode) {
+                       if (update)
+                               unlink_entry(ce->name);
+                       continue;
+               }
+               if (ce->ce_flags & mask) {
+                       ce->ce_flags &= ~mask;
+                       if (update)
+                               checkout_entry(ce, &state, NULL);
+               }
+       }
+       if (total) {
+               signal(SIGALRM, SIG_IGN);
+               fputc('\n', stderr);
+       }
+ }
+ static int unpack_trees(merge_fn_t fn)
+ {
+       int indpos = 0;
+       unsigned len = object_list_length(trees);
+       struct tree_entry_list **posns;
+       int i;
+       struct object_list *posn = trees;
+       merge_size = len;
+       if (len) {
+               posns = xmalloc(len * sizeof(struct tree_entry_list *));
+               for (i = 0; i < len; i++) {
+                       posns[i] = ((struct tree *) posn->item)->entries;
+                       posn = posn->next;
+               }
 -      else
++              if (unpack_trees_rec(posns, len, prefix ? prefix : "",
++                                   fn, &indpos))
+                       return -1;
+       }
+       if (trivial_merges_only && nontrivial_merge)
+               die("Merge requires file-level merging");
+       check_updates(active_cache, active_nr);
+       return 0;
+ }
+ static int list_tree(unsigned char *sha1)
+ {
+       struct tree *tree = parse_tree_indirect(sha1);
+       if (!tree)
+               return -1;
+       object_list_append(&tree->object, &trees);
+       return 0;
+ }
+ static int same(struct cache_entry *a, struct cache_entry *b)
+ {
+       if (!!a != !!b)
+               return 0;
+       if (!a && !b)
+               return 1;
+       return a->ce_mode == b->ce_mode && 
+               !memcmp(a->sha1, b->sha1, 20);
+ }
+ /*
+  * When a CE gets turned into an unmerged entry, we
+  * want it to be up-to-date
+  */
+ static void verify_uptodate(struct cache_entry *ce)
+ {
+       struct stat st;
+       if (index_only || reset)
+               return;
+       if (!lstat(ce->name, &st)) {
+               unsigned changed = ce_match_stat(ce, &st, 1);
+               if (!changed)
+                       return;
+               errno = 0;
+       }
+       if (reset) {
+               ce->ce_flags |= htons(CE_UPDATE);
+               return;
+       }
+       if (errno == ENOENT)
+               return;
+       die("Entry '%s' not uptodate. Cannot merge.", ce->name);
+ }
++static void invalidate_ce_path(struct cache_entry *ce)
++{
++      if (ce)
++              cache_tree_invalidate_path(active_cache_tree, ce->name);
++}
++
+ /*
+  * We do not want to remove or overwrite a working tree file that
+  * is not tracked.
+  */
+ static void verify_absent(const char *path, const char *action)
+ {
+       struct stat st;
+       if (index_only || reset || !update)
+               return;
+       if (!lstat(path, &st))
+               die("Untracked working tree file '%s' "
+                   "would be %s by merge.", path, action);
+ }
+ static int merged_entry(struct cache_entry *merge, struct cache_entry *old)
+ {
+       merge->ce_flags |= htons(CE_UPDATE);
+       if (old) {
+               /*
+                * See if we can re-use the old CE directly?
+                * That way we get the uptodate stat info.
+                *
+                * This also removes the UPDATE flag on
+                * a match.
+                */
+               if (same(old, merge)) {
+                       *merge = *old;
+               } else {
+                       verify_uptodate(old);
++                      invalidate_ce_path(old);
+               }
+       }
 -      if (!a)
++      else {
+               verify_absent(merge->name, "overwritten");
++              invalidate_ce_path(merge);
++      }
+       merge->ce_flags &= ~htons(CE_STAGEMASK);
+       add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
+       return 1;
+ }
+ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old)
+ {
+       if (old)
+               verify_uptodate(old);
+       else
+               verify_absent(ce->name, "removed");
+       ce->ce_mode = 0;
+       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
++      invalidate_ce_path(ce);
+       return 1;
+ }
+ static int keep_entry(struct cache_entry *ce)
+ {
+       add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+       return 1;
+ }
+ #if DBRT_DEBUG
+ static void show_stage_entry(FILE *o,
+                            const char *label, const struct cache_entry *ce)
+ {
+       if (!ce)
+               fprintf(o, "%s (missing)\n", label);
+       else
+               fprintf(o, "%s%06o %s %d\t%s\n",
+                       label,
+                       ntohl(ce->ce_mode),
+                       sha1_to_hex(ce->sha1),
+                       ce_stage(ce),
+                       ce->name);
+ }
+ #endif
+ static int threeway_merge(struct cache_entry **stages)
+ {
+       struct cache_entry *index;
+       struct cache_entry *head; 
+       struct cache_entry *remote = stages[head_idx + 1];
+       int count;
+       int head_match = 0;
+       int remote_match = 0;
+       const char *path = NULL;
+       int df_conflict_head = 0;
+       int df_conflict_remote = 0;
+       int any_anc_missing = 0;
+       int no_anc_exists = 1;
+       int i;
+       for (i = 1; i < head_idx; i++) {
+               if (!stages[i])
+                       any_anc_missing = 1;
+               else {
+                       if (!path)
+                               path = stages[i]->name;
+                       no_anc_exists = 0;
+               }
+       }
+       index = stages[0];
+       head = stages[head_idx];
+       if (head == &df_conflict_entry) {
+               df_conflict_head = 1;
+               head = NULL;
+       }
+       if (remote == &df_conflict_entry) {
+               df_conflict_remote = 1;
+               remote = NULL;
+       }
+       if (!path && index)
+               path = index->name;
+       if (!path && head)
+               path = head->name;
+       if (!path && remote)
+               path = remote->name;
+       /* First, if there's a #16 situation, note that to prevent #13
+        * and #14.
+        */
+       if (!same(remote, head)) {
+               for (i = 1; i < head_idx; i++) {
+                       if (same(stages[i], head)) {
+                               head_match = i;
+                       }
+                       if (same(stages[i], remote)) {
+                               remote_match = i;
+                       }
+               }
+       }
+       /* We start with cases where the index is allowed to match
+        * something other than the head: #14(ALT) and #2ALT, where it
+        * is permitted to match the result instead.
+        */
+       /* #14, #14ALT, #2ALT */
+       if (remote && !df_conflict_head && head_match && !remote_match) {
+               if (index && !same(index, remote) && !same(index, head))
+                       reject_merge(index);
+               return merged_entry(remote, index);
+       }
+       /*
+        * If we have an entry in the index cache, then we want to
+        * make sure that it matches head.
+        */
+       if (index && !same(index, head)) {
+               reject_merge(index);
+       }
+       if (head) {
+               /* #5ALT, #15 */
+               if (same(head, remote))
+                       return merged_entry(head, index);
+               /* #13, #3ALT */
+               if (!df_conflict_remote && remote_match && !head_match)
+                       return merged_entry(head, index);
+       }
+       /* #1 */
+       if (!head && !remote && any_anc_missing)
+               return 0;
+       /* Under the new "aggressive" rule, we resolve mostly trivial
+        * cases that we historically had git-merge-one-file resolve.
+        */
+       if (aggressive) {
+               int head_deleted = !head && !df_conflict_head;
+               int remote_deleted = !remote && !df_conflict_remote;
+               /*
+                * Deleted in both.
+                * Deleted in one and unchanged in the other.
+                */
+               if ((head_deleted && remote_deleted) ||
+                   (head_deleted && remote && remote_match) ||
+                   (remote_deleted && head && head_match)) {
+                       if (index)
+                               return deleted_entry(index, index);
+                       else if (path)
+                               verify_absent(path, "removed");
+                       return 0;
+               }
+               /*
+                * Added in both, identically.
+                */
+               if (no_anc_exists && head && remote && same(head, remote))
+                       return merged_entry(head, index);
+       }
+       /* Below are "no merge" cases, which require that the index be
+        * up-to-date to avoid the files getting overwritten with
+        * conflict resolution files. 
+        */
+       if (index) {
+               verify_uptodate(index);
+       }
+       else if (path)
+               verify_absent(path, "overwritten");
+       nontrivial_merge = 1;
+       /* #2, #3, #4, #6, #7, #9, #11. */
+       count = 0;
+       if (!head_match || !remote_match) {
+               for (i = 1; i < head_idx; i++) {
+                       if (stages[i]) {
+                               keep_entry(stages[i]);
+                               count++;
+                               break;
+                       }
+               }
+       }
+ #if DBRT_DEBUG
+       else {
+               fprintf(stderr, "read-tree: warning #16 detected\n");
+               show_stage_entry(stderr, "head   ", stages[head_match]);
+               show_stage_entry(stderr, "remote ", stages[remote_match]);
+       }
+ #endif
+       if (head) { count += keep_entry(head); }
+       if (remote) { count += keep_entry(remote); }
+       return count;
+ }
+ /*
+  * Two-way merge.
+  *
+  * The rule is to "carry forward" what is in the index without losing
+  * information across a "fast forward", favoring a successful merge
+  * over a merge failure when it makes sense.  For details of the
+  * "carry forward" rule, please see <Documentation/git-read-tree.txt>.
+  *
+  */
+ static int twoway_merge(struct cache_entry **src)
+ {
+       struct cache_entry *current = src[0];
+       struct cache_entry *oldtree = src[1], *newtree = src[2];
+       if (merge_size != 2)
+               return error("Cannot do a twoway merge of %d trees",
+                            merge_size);
+       if (current) {
+               if ((!oldtree && !newtree) || /* 4 and 5 */
+                   (!oldtree && newtree &&
+                    same(current, newtree)) || /* 6 and 7 */
+                   (oldtree && newtree &&
+                    same(oldtree, newtree)) || /* 14 and 15 */
+                   (oldtree && newtree &&
+                    !same(oldtree, newtree) && /* 18 and 19*/
+                    same(current, newtree))) {
+                       return keep_entry(current);
+               }
+               else if (oldtree && !newtree && same(current, oldtree)) {
+                       /* 10 or 11 */
+                       return deleted_entry(oldtree, current);
+               }
+               else if (oldtree && newtree &&
+                        same(current, oldtree) && !same(current, newtree)) {
+                       /* 20 or 21 */
+                       return merged_entry(newtree, current);
+               }
+               else {
+                       /* all other failures */
+                       if (oldtree)
+                               reject_merge(oldtree);
+                       if (current)
+                               reject_merge(current);
+                       if (newtree)
+                               reject_merge(newtree);
+                       return -1;
+               }
+       }
+       else if (newtree)
+               return merged_entry(newtree, current);
+       else
+               return deleted_entry(oldtree, current);
+ }
+ /*
++ * Bind merge.
++ *
++ * Keep the index entries at stage0, collapse stage1 but make sure
++ * stage0 does not have anything there.
++ */
++static int bind_merge(struct cache_entry **src)
++{
++      struct cache_entry *old = src[0];
++      struct cache_entry *a = src[1];
++
++      if (merge_size != 1)
++              return error("Cannot do a bind merge of %d trees\n",
++                           merge_size);
++      if (a && old)
++              die("Entry '%s' overlaps.  Cannot bind.", a->name);
++      if (!a)
++              return keep_entry(old);
++      else
++              return merged_entry(a, NULL);
++}
++
++/*
+  * One-way merge.
+  *
+  * The rule is:
+  * - take the stat information from stage0, take the data from stage1
+  */
+ static int oneway_merge(struct cache_entry **src)
+ {
+       struct cache_entry *old = src[0];
+       struct cache_entry *a = src[1];
+       if (merge_size != 1)
+               return error("Cannot do a oneway merge of %d trees",
+                            merge_size);
 -static const char read_tree_usage[] = "git-read-tree (<sha> | -m [--aggressive] [-u | -i] <sha1> [<sha2> [<sha3>]])";
++      if (!a) {
++              invalidate_ce_path(old);
+               return deleted_entry(old, old);
++      }
+       if (old && same(old, a)) {
+               if (reset) {
+                       struct stat st;
+                       if (lstat(old->name, &st) ||
+                           ce_match_stat(old, &st, 1))
+                               old->ce_flags |= htons(CE_UPDATE);
+               }
+               return keep_entry(old);
+       }
+       return merged_entry(a, old);
+ }
+ static int read_cache_unmerged(void)
+ {
+       int i, deleted;
+       struct cache_entry **dst;
+       read_cache();
+       dst = active_cache;
+       deleted = 0;
+       for (i = 0; i < active_nr; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce_stage(ce)) {
+                       deleted++;
++                      invalidate_ce_path(ce);
+                       continue;
+               }
+               if (deleted)
+                       *dst = ce;
+               dst++;
+       }
+       active_nr -= deleted;
+       return deleted;
+ }
 -                      if (stage || merge)
++static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
++{
++      struct tree_entry_list *ent;
++      int cnt;
++
++      memcpy(it->sha1, tree->object.sha1, 20);
++      for (cnt = 0, ent = tree->entries; ent; ent = ent->next) {
++              if (!ent->directory)
++                      cnt++;
++              else {
++                      struct cache_tree_sub *sub;
++                      struct tree *subtree = (struct tree *)ent->item.tree;
++                      if (!subtree->object.parsed)
++                              parse_tree(subtree);
++                      sub = cache_tree_sub(it, ent->name);
++                      sub->cache_tree = cache_tree();
++                      prime_cache_tree_rec(sub->cache_tree, subtree);
++                      cnt += sub->cache_tree->entry_count;
++              }
++      }
++      it->entry_count = cnt;
++}
++
++static void prime_cache_tree(void)
++{
++      struct tree *tree = (struct tree *)trees->item;
++      if (!tree)
++              return;
++      active_cache_tree = cache_tree();
++      prime_cache_tree_rec(active_cache_tree, tree);
++
++}
++
++static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] <sha1> [<sha2> [<sha3>]])";
+ static struct cache_file cache_file;
+ int cmd_read_tree(int argc, const char **argv, char **envp)
+ {
+       int i, newfd, stage = 0;
+       unsigned char sha1[20];
+       merge_fn_t fn = NULL;
+       setup_git_directory();
+       git_config(git_default_config);
+       newfd = hold_index_file_for_update(&cache_file, get_index_file());
+       if (newfd < 0)
+               die("unable to create new cachefile");
+       git_config(git_default_config);
+       merge = 0;
+       reset = 0;
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               /* "-u" means "update", meaning that a merge will update
+                * the working tree.
+                */
+               if (!strcmp(arg, "-u")) {
+                       update = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-v")) {
+                       verbose_update = 1;
+                       continue;
+               }
+               /* "-i" means "index only", meaning that a merge will
+                * not even look at the working tree.
+                */
+               if (!strcmp(arg, "-i")) {
+                       index_only = 1;
+                       continue;
+               }
++              /* "--prefix=<subdirectory>/" means keep the current index
++               *  entries and put the entries from the tree under the
++               * given subdirectory.
++               */
++              if (!strncmp(arg, "--prefix=", 9)) {
++                      if (stage || merge || prefix)
++                              usage(read_tree_usage);
++                      prefix = arg + 9;
++                      merge = 1;
++                      stage = 1;
++                      if (read_cache_unmerged())
++                              die("you need to resolve your current index first");
++                      continue;
++              }
++
+               /* This differs from "-m" in that we'll silently ignore unmerged entries */
+               if (!strcmp(arg, "--reset")) {
 -                      if (stage || merge)
++                      if (stage || merge || prefix)
+                               usage(read_tree_usage);
+                       reset = 1;
+                       merge = 1;
+                       stage = 1;
+                       read_cache_unmerged();
+                       continue;
+               }
+               if (!strcmp(arg, "--trivial")) {
+                       trivial_merges_only = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--aggressive")) {
+                       aggressive = 1;
+                       continue;
+               }
+               /* "-m" stands for "merge", meaning we start in stage 1 */
+               if (!strcmp(arg, "-m")) {
 -                      fn = oneway_merge;
++                      if (stage || merge || prefix)
+                               usage(read_tree_usage);
+                       if (read_cache_unmerged())
+                               die("you need to resolve your current index first");
+                       stage = 1;
+                       merge = 1;
+                       continue;
+               }
+               /* using -u and -i at the same time makes no sense */
+               if (1 < index_only + update)
+                       usage(read_tree_usage);
+               if (get_sha1(arg, sha1))
+                       die("Not a valid object name %s", arg);
+               if (list_tree(sha1) < 0)
+                       die("failed to unpack tree object %s", arg);
+               stage++;
+       }
+       if ((update||index_only) && !merge)
+               usage(read_tree_usage);
++      if (prefix) {
++              int pfxlen = strlen(prefix);
++              int pos;
++              if (prefix[pfxlen-1] != '/')
++                      die("prefix must end with /");
++              if (stage != 2)
++                      die("binding merge takes only one tree");
++              pos = cache_name_pos(prefix, pfxlen);
++              if (0 <= pos)
++                      die("corrupt index file");
++              pos = -pos-1;
++              if (pos < active_nr &&
++                  !strncmp(active_cache[pos]->name, prefix, pfxlen))
++                      die("subdirectory '%s' already exists.", prefix);
++              pos = cache_name_pos(prefix, pfxlen-1);
++              if (0 <= pos)
++                      die("file '%.*s' already exists.", pfxlen-1, prefix);
++      }
++
+       if (merge) {
+               if (stage < 2)
+                       die("just how do you expect me to merge %d trees?", stage-1);
+               switch (stage - 1) {
+               case 1:
 -                      fn = threeway_merge;
 -                      break;
++                      fn = prefix ? bind_merge : oneway_merge;
+                       break;
+               case 2:
+                       fn = twoway_merge;
+                       break;
+               case 3:
+               default:
+                       fn = threeway_merge;
++                      cache_tree_free(&active_cache_tree);
+                       break;
+               }
+               if (stage - 1 >= 3)
+                       head_idx = stage - 2;
+               else
+                       head_idx = 1;
+       }
+       unpack_trees(fn);
++
++      /*
++       * When reading only one tree (either the most basic form,
++       * "-m ent" or "--reset ent" form), we can obtain a fully
++       * valid cache-tree because the index must match exactly
++       * what came from the tree.
++       */
++      if (trees && trees->item && (!merge || (stage == 2))) {
++              cache_tree_free(&active_cache_tree);
++              prime_cache_tree();
++      }
++
+       if (write_cache(newfd, active_cache, active_nr) ||
+           commit_index_file(&cache_file))
+               die("unable to write new index file");
+       return 0;
+ }
index 0000000,3af24e7..2895140
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,789 +1,789 @@@
 -                                  pretty, sizeof(pretty), 0);
+ #include <stdlib.h>
+ #include <fnmatch.h>
+ #include "cache.h"
+ #include "commit.h"
+ #include "refs.h"
+ #include "builtin.h"
+ static const char show_branch_usage[] =
+ "git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
+ static int default_num = 0;
+ static int default_alloc = 0;
+ static const char **default_arg = NULL;
+ #define UNINTERESTING 01
+ #define REV_SHIFT      2
+ #define MAX_REVS      29 /* should not exceed bits_per_int - REV_SHIFT */
+ static struct commit *interesting(struct commit_list *list)
+ {
+       while (list) {
+               struct commit *commit = list->item;
+               list = list->next;
+               if (commit->object.flags & UNINTERESTING)
+                       continue;
+               return commit;
+       }
+       return NULL;
+ }
+ static struct commit *pop_one_commit(struct commit_list **list_p)
+ {
+       struct commit *commit;
+       struct commit_list *list;
+       list = *list_p;
+       commit = list->item;
+       *list_p = list->next;
+       free(list);
+       return commit;
+ }
+ struct commit_name {
+       const char *head_name; /* which head's ancestor? */
+       int generation; /* how many parents away from head_name */
+ };
+ /* Name the commit as nth generation ancestor of head_name;
+  * we count only the first-parent relationship for naming purposes.
+  */
+ static void name_commit(struct commit *commit, const char *head_name, int nth)
+ {
+       struct commit_name *name;
+       if (!commit->object.util)
+               commit->object.util = xmalloc(sizeof(struct commit_name));
+       name = commit->object.util;
+       name->head_name = head_name;
+       name->generation = nth;
+ }
+ /* Parent is the first parent of the commit.  We may name it
+  * as (n+1)th generation ancestor of the same head_name as
+  * commit is nth generation ancestor of, if that generation
+  * number is better than the name it already has.
+  */
+ static void name_parent(struct commit *commit, struct commit *parent)
+ {
+       struct commit_name *commit_name = commit->object.util;
+       struct commit_name *parent_name = parent->object.util;
+       if (!commit_name)
+               return;
+       if (!parent_name ||
+           commit_name->generation + 1 < parent_name->generation)
+               name_commit(parent, commit_name->head_name,
+                           commit_name->generation + 1);
+ }
+ static int name_first_parent_chain(struct commit *c)
+ {
+       int i = 0;
+       while (c) {
+               struct commit *p;
+               if (!c->object.util)
+                       break;
+               if (!c->parents)
+                       break;
+               p = c->parents->item;
+               if (!p->object.util) {
+                       name_parent(c, p);
+                       i++;
+               }
+               c = p;
+       }
+       return i;
+ }
+ static void name_commits(struct commit_list *list,
+                        struct commit **rev,
+                        char **ref_name,
+                        int num_rev)
+ {
+       struct commit_list *cl;
+       struct commit *c;
+       int i;
+       /* First give names to the given heads */
+       for (cl = list; cl; cl = cl->next) {
+               c = cl->item;
+               if (c->object.util)
+                       continue;
+               for (i = 0; i < num_rev; i++) {
+                       if (rev[i] == c) {
+                               name_commit(c, ref_name[i], 0);
+                               break;
+                       }
+               }
+       }
+       /* Then commits on the first parent ancestry chain */
+       do {
+               i = 0;
+               for (cl = list; cl; cl = cl->next) {
+                       i += name_first_parent_chain(cl->item);
+               }
+       } while (i);
+       /* Finally, any unnamed commits */
+       do {
+               i = 0;
+               for (cl = list; cl; cl = cl->next) {
+                       struct commit_list *parents;
+                       struct commit_name *n;
+                       int nth;
+                       c = cl->item;
+                       if (!c->object.util)
+                               continue;
+                       n = c->object.util;
+                       parents = c->parents;
+                       nth = 0;
+                       while (parents) {
+                               struct commit *p = parents->item;
+                               char newname[1000], *en;
+                               parents = parents->next;
+                               nth++;
+                               if (p->object.util)
+                                       continue;
+                               en = newname;
+                               switch (n->generation) {
+                               case 0:
+                                       en += sprintf(en, "%s", n->head_name);
+                                       break;
+                               case 1:
+                                       en += sprintf(en, "%s^", n->head_name);
+                                       break;
+                               default:
+                                       en += sprintf(en, "%s~%d",
+                                               n->head_name, n->generation);
+                                       break;
+                               }
+                               if (nth == 1)
+                                       en += sprintf(en, "^");
+                               else
+                                       en += sprintf(en, "^%d", nth);
+                               name_commit(p, strdup(newname), 0);
+                               i++;
+                               name_first_parent_chain(p);
+                       }
+               }
+       } while (i);
+ }
+ static int mark_seen(struct commit *commit, struct commit_list **seen_p)
+ {
+       if (!commit->object.flags) {
+               insert_by_date(commit, seen_p);
+               return 1;
+       }
+       return 0;
+ }
+ static void join_revs(struct commit_list **list_p,
+                     struct commit_list **seen_p,
+                     int num_rev, int extra)
+ {
+       int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+       int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+       while (*list_p) {
+               struct commit_list *parents;
+               int still_interesting = !!interesting(*list_p);
+               struct commit *commit = pop_one_commit(list_p);
+               int flags = commit->object.flags & all_mask;
+               if (!still_interesting && extra <= 0)
+                       break;
+               mark_seen(commit, seen_p);
+               if ((flags & all_revs) == all_revs)
+                       flags |= UNINTERESTING;
+               parents = commit->parents;
+               while (parents) {
+                       struct commit *p = parents->item;
+                       int this_flag = p->object.flags;
+                       parents = parents->next;
+                       if ((this_flag & flags) == flags)
+                               continue;
+                       if (!p->object.parsed)
+                               parse_commit(p);
+                       if (mark_seen(p, seen_p) && !still_interesting)
+                               extra--;
+                       p->object.flags |= flags;
+                       insert_by_date(p, list_p);
+               }
+       }
+       /*
+        * Postprocess to complete well-poisoning.
+        *
+        * At this point we have all the commits we have seen in
+        * seen_p list (which happens to be sorted chronologically but
+        * it does not really matter).  Mark anything that can be
+        * reached from uninteresting commits not interesting.
+        */
+       for (;;) {
+               int changed = 0;
+               struct commit_list *s;
+               for (s = *seen_p; s; s = s->next) {
+                       struct commit *c = s->item;
+                       struct commit_list *parents;
+                       if (((c->object.flags & all_revs) != all_revs) &&
+                           !(c->object.flags & UNINTERESTING))
+                               continue;
+                       /* The current commit is either a merge base or
+                        * already uninteresting one.  Mark its parents
+                        * as uninteresting commits _only_ if they are
+                        * already parsed.  No reason to find new ones
+                        * here.
+                        */
+                       parents = c->parents;
+                       while (parents) {
+                               struct commit *p = parents->item;
+                               parents = parents->next;
+                               if (!(p->object.flags & UNINTERESTING)) {
+                                       p->object.flags |= UNINTERESTING;
+                                       changed = 1;
+                               }
+                       }
+               }
+               if (!changed)
+                       break;
+       }
+ }
+ static void show_one_commit(struct commit *commit, int no_name)
+ {
+       char pretty[256], *cp;
+       struct commit_name *name = commit->object.util;
+       if (commit->object.parsed)
+               pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
++                                  pretty, sizeof(pretty), 0, NULL, NULL);
+       else
+               strcpy(pretty, "(unavailable)");
+       if (!strncmp(pretty, "[PATCH] ", 8))
+               cp = pretty + 8;
+       else
+               cp = pretty;
+       if (!no_name) {
+               if (name && name->head_name) {
+                       printf("[%s", name->head_name);
+                       if (name->generation) {
+                               if (name->generation == 1)
+                                       printf("^");
+                               else
+                                       printf("~%d", name->generation);
+                       }
+                       printf("] ");
+               }
+               else
+                       printf("[%s] ",
+                              find_unique_abbrev(commit->object.sha1, 7));
+       }
+       puts(cp);
+ }
+ static char *ref_name[MAX_REVS + 1];
+ static int ref_name_cnt;
+ static const char *find_digit_prefix(const char *s, int *v)
+ {
+       const char *p;
+       int ver;
+       char ch;
+       for (p = s, ver = 0;
+            '0' <= (ch = *p) && ch <= '9';
+            p++)
+               ver = ver * 10 + ch - '0';
+       *v = ver;
+       return p;
+ }
+ static int version_cmp(const char *a, const char *b)
+ {
+       while (1) {
+               int va, vb;
+               a = find_digit_prefix(a, &va);
+               b = find_digit_prefix(b, &vb);
+               if (va != vb)
+                       return va - vb;
+               while (1) {
+                       int ca = *a;
+                       int cb = *b;
+                       if ('0' <= ca && ca <= '9')
+                               ca = 0;
+                       if ('0' <= cb && cb <= '9')
+                               cb = 0;
+                       if (ca != cb)
+                               return ca - cb;
+                       if (!ca)
+                               break;
+                       a++;
+                       b++;
+               }
+               if (!*a && !*b)
+                       return 0;
+       }
+ }
+ static int compare_ref_name(const void *a_, const void *b_)
+ {
+       const char * const*a = a_, * const*b = b_;
+       return version_cmp(*a, *b);
+ }
+ static void sort_ref_range(int bottom, int top)
+ {
+       qsort(ref_name + bottom, top - bottom, sizeof(ref_name[0]),
+             compare_ref_name);
+ }
+ static int append_ref(const char *refname, const unsigned char *sha1)
+ {
+       struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+       int i;
+       if (!commit)
+               return 0;
+       /* Avoid adding the same thing twice */
+       for (i = 0; i < ref_name_cnt; i++)
+               if (!strcmp(refname, ref_name[i]))
+                       return 0;
+       if (MAX_REVS <= ref_name_cnt) {
+               fprintf(stderr, "warning: ignoring %s; "
+                       "cannot handle more than %d refs\n",
+                       refname, MAX_REVS);
+               return 0;
+       }
+       ref_name[ref_name_cnt++] = strdup(refname);
+       ref_name[ref_name_cnt] = NULL;
+       return 0;
+ }
+ static int append_head_ref(const char *refname, const unsigned char *sha1)
+ {
+       unsigned char tmp[20];
+       int ofs = 11;
+       if (strncmp(refname, "refs/heads/", ofs))
+               return 0;
+       /* If both heads/foo and tags/foo exists, get_sha1 would
+        * get confused.
+        */
+       if (get_sha1(refname + ofs, tmp) || memcmp(tmp, sha1, 20))
+               ofs = 5;
+       return append_ref(refname + ofs, sha1);
+ }
+ static int append_tag_ref(const char *refname, const unsigned char *sha1)
+ {
+       if (strncmp(refname, "refs/tags/", 10))
+               return 0;
+       return append_ref(refname + 5, sha1);
+ }
+ static const char *match_ref_pattern = NULL;
+ static int match_ref_slash = 0;
+ static int count_slash(const char *s)
+ {
+       int cnt = 0;
+       while (*s)
+               if (*s++ == '/')
+                       cnt++;
+       return cnt;
+ }
+ static int append_matching_ref(const char *refname, const unsigned char *sha1)
+ {
+       /* we want to allow pattern hold/<asterisk> to show all
+        * branches under refs/heads/hold/, and v0.99.9? to show
+        * refs/tags/v0.99.9a and friends.
+        */
+       const char *tail;
+       int slash = count_slash(refname);
+       for (tail = refname; *tail && match_ref_slash < slash; )
+               if (*tail++ == '/')
+                       slash--;
+       if (!*tail)
+               return 0;
+       if (fnmatch(match_ref_pattern, tail, 0))
+               return 0;
+       if (!strncmp("refs/heads/", refname, 11))
+               return append_head_ref(refname, sha1);
+       if (!strncmp("refs/tags/", refname, 10))
+               return append_tag_ref(refname, sha1);
+       return append_ref(refname, sha1);
+ }
+ static void snarf_refs(int head, int tag)
+ {
+       if (head) {
+               int orig_cnt = ref_name_cnt;
+               for_each_ref(append_head_ref);
+               sort_ref_range(orig_cnt, ref_name_cnt);
+       }
+       if (tag) {
+               int orig_cnt = ref_name_cnt;
+               for_each_ref(append_tag_ref);
+               sort_ref_range(orig_cnt, ref_name_cnt);
+       }
+ }
+ static int rev_is_head(char *head_path, int headlen, char *name,
+                      unsigned char *head_sha1, unsigned char *sha1)
+ {
+       int namelen;
+       if ((!head_path[0]) ||
+           (head_sha1 && sha1 && memcmp(head_sha1, sha1, 20)))
+               return 0;
+       namelen = strlen(name);
+       if ((headlen < namelen) ||
+           memcmp(head_path + headlen - namelen, name, namelen))
+               return 0;
+       if (headlen == namelen ||
+           head_path[headlen - namelen - 1] == '/')
+               return 1;
+       return 0;
+ }
+ static int show_merge_base(struct commit_list *seen, int num_rev)
+ {
+       int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+       int all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+       int exit_status = 1;
+       while (seen) {
+               struct commit *commit = pop_one_commit(&seen);
+               int flags = commit->object.flags & all_mask;
+               if (!(flags & UNINTERESTING) &&
+                   ((flags & all_revs) == all_revs)) {
+                       puts(sha1_to_hex(commit->object.sha1));
+                       exit_status = 0;
+                       commit->object.flags |= UNINTERESTING;
+               }
+       }
+       return exit_status;
+ }
+ static int show_independent(struct commit **rev,
+                           int num_rev,
+                           char **ref_name,
+                           unsigned int *rev_mask)
+ {
+       int i;
+       for (i = 0; i < num_rev; i++) {
+               struct commit *commit = rev[i];
+               unsigned int flag = rev_mask[i];
+               if (commit->object.flags == flag)
+                       puts(sha1_to_hex(commit->object.sha1));
+               commit->object.flags |= UNINTERESTING;
+       }
+       return 0;
+ }
+ static void append_one_rev(const char *av)
+ {
+       unsigned char revkey[20];
+       if (!get_sha1(av, revkey)) {
+               append_ref(av, revkey);
+               return;
+       }
+       if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
+               /* glob style match */
+               int saved_matches = ref_name_cnt;
+               match_ref_pattern = av;
+               match_ref_slash = count_slash(av);
+               for_each_ref(append_matching_ref);
+               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);
+ }
+ static int git_show_branch_config(const char *var, const char *value)
+ {
+       if (!strcmp(var, "showbranch.default")) {
+               if (default_alloc <= default_num + 1) {
+                       default_alloc = default_alloc * 3 / 2 + 20;
+                       default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);
+               }
+               default_arg[default_num++] = strdup(value);
+               default_arg[default_num] = NULL;
+               return 0;
+       }
+       return git_default_config(var, value);
+ }
+ static int omit_in_dense(struct commit *commit, struct commit **rev, int n)
+ {
+       /* If the commit is tip of the named branches, do not
+        * omit it.
+        * Otherwise, if it is a merge that is reachable from only one
+        * tip, it is not that interesting.
+        */
+       int i, flag, count;
+       for (i = 0; i < n; i++)
+               if (rev[i] == commit)
+                       return 0;
+       flag = commit->object.flags;
+       for (i = count = 0; i < n; i++) {
+               if (flag & (1u << (i + REV_SHIFT)))
+                       count++;
+       }
+       if (count == 1)
+               return 1;
+       return 0;
+ }
+ int cmd_show_branch(int ac, const char **av, char **envp)
+ {
+       struct commit *rev[MAX_REVS], *commit;
+       struct commit_list *list = NULL, *seen = NULL;
+       unsigned int rev_mask[MAX_REVS];
+       int num_rev, i, extra = 0;
+       int all_heads = 0, all_tags = 0;
+       int all_mask, all_revs;
+       int lifo = 1;
+       char head_path[128];
+       const char *head_path_p;
+       int head_path_len;
+       unsigned char head_sha1[20];
+       int merge_base = 0;
+       int independent = 0;
+       int no_name = 0;
+       int sha1_name = 0;
+       int shown_merge_point = 0;
+       int with_current_branch = 0;
+       int head_at = -1;
+       int topics = 0;
+       int dense = 1;
+       setup_git_directory();
+       git_config(git_show_branch_config);
+       /* If nothing is specified, try the default first */
+       if (ac == 1 && default_num) {
+               ac = default_num + 1;
+               av = default_arg - 1; /* ick; we would not address av[0] */
+       }
+       while (1 < ac && av[1][0] == '-') {
+               const char *arg = av[1];
+               if (!strcmp(arg, "--")) {
+                       ac--; av++;
+                       break;
+               }
+               else if (!strcmp(arg, "--all"))
+                       all_heads = all_tags = 1;
+               else if (!strcmp(arg, "--heads"))
+                       all_heads = 1;
+               else if (!strcmp(arg, "--tags"))
+                       all_tags = 1;
+               else if (!strcmp(arg, "--more"))
+                       extra = 1;
+               else if (!strcmp(arg, "--list"))
+                       extra = -1;
+               else if (!strcmp(arg, "--no-name"))
+                       no_name = 1;
+               else if (!strcmp(arg, "--current"))
+                       with_current_branch = 1;
+               else if (!strcmp(arg, "--sha1-name"))
+                       sha1_name = 1;
+               else if (!strncmp(arg, "--more=", 7))
+                       extra = atoi(arg + 7);
+               else if (!strcmp(arg, "--merge-base"))
+                       merge_base = 1;
+               else if (!strcmp(arg, "--independent"))
+                       independent = 1;
+               else if (!strcmp(arg, "--topo-order"))
+                       lifo = 1;
+               else if (!strcmp(arg, "--topics"))
+                       topics = 1;
+               else if (!strcmp(arg, "--sparse"))
+                       dense = 0;
+               else if (!strcmp(arg, "--date-order"))
+                       lifo = 0;
+               else
+                       usage(show_branch_usage);
+               ac--; av++;
+       }
+       ac--; av++;
+       /* Only one of these is allowed */
+       if (1 < independent + merge_base + (extra != 0))
+               usage(show_branch_usage);
+       /* If nothing is specified, show all branches by default */
+       if (ac + all_heads + all_tags == 0)
+               all_heads = 1;
+       if (all_heads + all_tags)
+               snarf_refs(all_heads, all_tags);
+       while (0 < ac) {
+               append_one_rev(*av);
+               ac--; av++;
+       }
+       head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
+       if (head_path_p) {
+               head_path_len = strlen(head_path_p);
+               memcpy(head_path, head_path_p, head_path_len + 1);
+       }
+       else {
+               head_path_len = 0;
+               head_path[0] = 0;
+       }
+       if (with_current_branch && head_path_p) {
+               int has_head = 0;
+               for (i = 0; !has_head && i < ref_name_cnt; i++) {
+                       /* We are only interested in adding the branch
+                        * HEAD points at.
+                        */
+                       if (rev_is_head(head_path,
+                                       head_path_len,
+                                       ref_name[i],
+                                       head_sha1, NULL))
+                               has_head++;
+               }
+               if (!has_head) {
+                       int pfxlen = strlen(git_path("refs/heads/"));
+                       append_one_rev(head_path + pfxlen);
+               }
+       }
+       if (!ref_name_cnt) {
+               fprintf(stderr, "No revs to be shown.\n");
+               exit(0);
+       }
+       for (num_rev = 0; ref_name[num_rev]; num_rev++) {
+               unsigned char revkey[20];
+               unsigned int flag = 1u << (num_rev + REV_SHIFT);
+               if (MAX_REVS <= num_rev)
+                       die("cannot handle more than %d revs.", MAX_REVS);
+               if (get_sha1(ref_name[num_rev], revkey))
+                       die("'%s' is not a valid ref.", ref_name[num_rev]);
+               commit = lookup_commit_reference(revkey);
+               if (!commit)
+                       die("cannot find commit %s (%s)",
+                           ref_name[num_rev], revkey);
+               parse_commit(commit);
+               mark_seen(commit, &seen);
+               /* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
+                * and so on.  REV_SHIFT bits from bit 0 are used for
+                * internal bookkeeping.
+                */
+               commit->object.flags |= flag;
+               if (commit->object.flags == flag)
+                       insert_by_date(commit, &list);
+               rev[num_rev] = commit;
+       }
+       for (i = 0; i < num_rev; i++)
+               rev_mask[i] = rev[i]->object.flags;
+       if (0 <= extra)
+               join_revs(&list, &seen, num_rev, extra);
+       if (merge_base)
+               return show_merge_base(seen, num_rev);
+       if (independent)
+               return show_independent(rev, num_rev, ref_name, rev_mask);
+       /* Show list; --more=-1 means list-only */
+       if (1 < num_rev || extra < 0) {
+               for (i = 0; i < num_rev; i++) {
+                       int j;
+                       int is_head = rev_is_head(head_path,
+                                                 head_path_len,
+                                                 ref_name[i],
+                                                 head_sha1,
+                                                 rev[i]->object.sha1);
+                       if (extra < 0)
+                               printf("%c [%s] ",
+                                      is_head ? '*' : ' ', ref_name[i]);
+                       else {
+                               for (j = 0; j < i; j++)
+                                       putchar(' ');
+                               printf("%c [%s] ",
+                                      is_head ? '*' : '!', ref_name[i]);
+                       }
+                       /* header lines never need name */
+                       show_one_commit(rev[i], 1);
+                       if (is_head)
+                               head_at = i;
+               }
+               if (0 <= extra) {
+                       for (i = 0; i < num_rev; i++)
+                               putchar('-');
+                       putchar('\n');
+               }
+       }
+       if (extra < 0)
+               exit(0);
+       /* Sort topologically */
+       sort_in_topological_order(&seen, lifo);
+       /* Give names to commits */
+       if (!sha1_name && !no_name)
+               name_commits(seen, rev, ref_name, num_rev);
+       all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
+       all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
+       while (seen) {
+               struct commit *commit = pop_one_commit(&seen);
+               int this_flag = commit->object.flags;
+               int is_merge_point = ((this_flag & all_revs) == all_revs);
+               shown_merge_point |= is_merge_point;
+               if (1 < num_rev) {
+                       int is_merge = !!(commit->parents &&
+                                         commit->parents->next);
+                       if (topics &&
+                           !is_merge_point &&
+                           (this_flag & (1u << REV_SHIFT)))
+                               continue;
+                       if (dense && is_merge &&
+                           omit_in_dense(commit, rev, num_rev))
+                               continue;
+                       for (i = 0; i < num_rev; i++) {
+                               int mark;
+                               if (!(this_flag & (1u << (i + REV_SHIFT))))
+                                       mark = ' ';
+                               else if (is_merge)
+                                       mark = '-';
+                               else if (i == head_at)
+                                       mark = '*';
+                               else
+                                       mark = '+';
+                               putchar(mark);
+                       }
+                       putchar(' ');
+               }
+               show_one_commit(commit, no_name);
+               if (shown_merge_point && --extra < 0)
+                       break;
+       }
+       return 0;
+ }
diff --cc builtin.h
Simple merge
diff --cc git.c
Simple merge