Merge branch 'jc/sha1-name-more'
authorJunio C Hamano <gitster@pobox.com>
Sun, 22 Jul 2012 19:55:07 +0000 (12:55 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 22 Jul 2012 19:55:07 +0000 (12:55 -0700)
Teaches the object name parser things like a "git describe" output
is always a commit object, "A" in "git log A" must be a committish,
and "A" and "B" in "git log A...B" both must be committish, etc., to
prolong the lifetime of abbreviated object names.

* jc/sha1-name-more: (27 commits)
t1512: match the "other" object names
t1512: ignore whitespaces in wc -l output
rev-parse --disambiguate=<prefix>
rev-parse: A and B in "rev-parse A..B" refer to committish
reset: the command takes committish
commit-tree: the command wants a tree and commits
apply: --build-fake-ancestor expects blobs
sha1_name.c: add support for disambiguating other types
revision.c: the "log" family, except for "show", takes committish
revision.c: allow handle_revision_arg() to take other flags
sha1_name.c: introduce get_sha1_committish()
sha1_name.c: teach lookup context to get_sha1_with_context()
sha1_name.c: many short names can only be committish
sha1_name.c: get_sha1_1() takes lookup flags
sha1_name.c: get_describe_name() by definition groks only commits
sha1_name.c: teach get_short_sha1() a commit-only option
sha1_name.c: allow get_short_sha1() to take other flags
get_sha1(): fix error status regression
sha1_name.c: restructure disambiguation of short names
sha1_name.c: correct misnamed "canonical" and "res"
...

13 files changed:
1  2 
Documentation/git-rev-parse.txt
builtin/apply.c
builtin/cat-file.c
builtin/log.c
builtin/pack-objects.c
builtin/reset.c
builtin/rev-parse.c
cache.h
commit.c
revision.c
revision.h
setup.c
sha1_name.c
index 4cc3e9586fcfd79ddeb7f636141e1a878285b0b5,8d90863ee3aa43c24170abd3118f4e00e5d4a208..3c63561f02aca6f934c47a2897f8e50cf4ac5fe5
@@@ -101,6 -101,12 +101,12 @@@ OPTION
        The option core.warnAmbiguousRefs is used to select the strict
        abbreviation mode.
  
+ --disambiguate=<prefix>::
+       Show every object whose name begins with the given prefix.
+       The <prefix> must be at least 4 hexadecimal digits long to
+       avoid listing each and every object in the repository by
+       mistake.
  --all::
        Show all refs found in `refs/`.
  
  +
  If a `pattern` is given, only refs matching the given shell glob are
  shown.  If the pattern does not contain a globbing character (`?`,
 -`{asterisk}`, or `[`), it is turned into a prefix match by
 -appending `/{asterisk}`.
 +`*`, or `[`), it is turned into a prefix match by appending `/*`.
  
  --glob=pattern::
        Show all refs matching the shell glob pattern `pattern`. If
        the pattern does not start with `refs/`, this is automatically
        prepended.  If the pattern does not contain a globbing
 -      character (`?`, `{asterisk}`, or `[`), it is turned into a prefix
 -      match by appending `/{asterisk}`.
 +      character (`?`, `*`, or `[`), it is turned into a prefix
 +      match by appending `/*`.
  
  --show-toplevel::
        Show the absolute path of the top-level directory.
  
  --git-dir::
        Show `$GIT_DIR` if defined. Otherwise show the path to
 -      the .git directory, relative to the current directory.
 +      the .git directory. The path shown, when relative, is
 +      relative to the current working directory.
  +
  If `$GIT_DIR` is not defined and the current directory
  is not detected to lie in a git repository or work tree
diff --combined builtin/apply.c
index ace04c453be447b62b4907c45247f82ef4344033,8b2db1d76b60638e706c12f0cdfb264f089f0588..069cf341b653080e2bae64faa0a00b387f88c961
  #include "builtin.h"
  #include "string-list.h"
  #include "dir.h"
 +#include "diff.h"
  #include "parse-options.h"
 +#include "xdiff-interface.h"
 +#include "ll-merge.h"
 +#include "rerere.h"
  
  /*
   *  --check turns on checking that the working tree matches the
@@@ -49,12 -45,11 +49,12 @@@ static int apply_with_reject
  static int apply_verbosely;
  static int allow_overlap;
  static int no_add;
 +static int threeway;
  static const char *fake_ancestor;
  static int line_termination = '\n';
  static unsigned int p_context = UINT_MAX;
  static const char * const apply_usage[] = {
 -      "git apply [options] [<patch>...]",
 +      N_("git apply [options] [<patch>...]"),
        NULL
  };
  
@@@ -107,7 -102,7 +107,7 @@@ static void parse_whitespace_option(con
                ws_error_action = correct_ws_error;
                return;
        }
 -      die("unrecognized whitespace option '%s'", option);
 +      die(_("unrecognized whitespace option '%s'"), option);
  }
  
  static void parse_ignorewhitespace_option(const char *option)
                ws_ignore_action = ignore_ws_change;
                return;
        }
 -      die("unrecognized whitespace ignore option '%s'", option);
 +      die(_("unrecognized whitespace ignore option '%s'"), option);
  }
  
  static void set_default_whitespace_mode(const char *whitespace_option)
@@@ -156,14 -151,9 +156,14 @@@ struct fragment 
        unsigned long leading, trailing;
        unsigned long oldpos, oldlines;
        unsigned long newpos, newlines;
 +      /*
 +       * 'patch' is usually borrowed from buf in apply_patch(),
 +       * but some codepaths store an allocated buffer.
 +       */
        const char *patch;
 +      unsigned free_patch:1,
 +              rejected:1;
        int size;
 -      int rejected;
        int linenr;
        struct fragment *next;
  };
@@@ -197,49 -187,14 +197,49 @@@ struct patch 
        unsigned int is_copy:1;
        unsigned int is_rename:1;
        unsigned int recount:1;
 +      unsigned int conflicted_threeway:1;
 +      unsigned int direct_to_threeway:1;
        struct fragment *fragments;
        char *result;
        size_t resultsize;
        char old_sha1_prefix[41];
        char new_sha1_prefix[41];
        struct patch *next;
 +
 +      /* three-way fallback result */
 +      unsigned char threeway_stage[3][20];
  };
  
 +static void free_fragment_list(struct fragment *list)
 +{
 +      while (list) {
 +              struct fragment *next = list->next;
 +              if (list->free_patch)
 +                      free((char *)list->patch);
 +              free(list);
 +              list = next;
 +      }
 +}
 +
 +static void free_patch(struct patch *patch)
 +{
 +      free_fragment_list(patch->fragments);
 +      free(patch->def_name);
 +      free(patch->old_name);
 +      free(patch->new_name);
 +      free(patch->result);
 +      free(patch);
 +}
 +
 +static void free_patch_list(struct patch *list)
 +{
 +      while (list) {
 +              struct patch *next = list->next;
 +              free_patch(list);
 +              list = next;
 +      }
 +}
 +
  /*
   * A line in a file, len-bytes long (includes the terminating LF,
   * except for an incomplete line at the end if the file ends with
@@@ -346,11 -301,6 +346,11 @@@ static void add_line_info(struct image 
        img->nr++;
  }
  
 +/*
 + * "buf" has the file contents to be patched (read from various sources).
 + * attach it to "image" and add line-based index to it.
 + * "image" now owns the "buf".
 + */
  static void prepare_image(struct image *image, char *buf, size_t len,
                          int prepare_linetable)
  {
  static void clear_image(struct image *image)
  {
        free(image->buf);
 -      image->buf = NULL;
 -      image->len = 0;
 +      free(image->line_allocated);
 +      memset(image, 0, sizeof(*image));
  }
  
 -static void say_patch_name(FILE *output, const char *pre,
 -                         struct patch *patch, const char *post)
 +/* fmt must contain _one_ %s and no other substitution */
 +static void say_patch_name(FILE *output, const char *fmt, struct patch *patch)
  {
 -      fputs(pre, output);
 +      struct strbuf sb = STRBUF_INIT;
 +
        if (patch->old_name && patch->new_name &&
            strcmp(patch->old_name, patch->new_name)) {
 -              quote_c_style(patch->old_name, NULL, output, 0);
 -              fputs(" => ", output);
 -              quote_c_style(patch->new_name, NULL, output, 0);
 +              quote_c_style(patch->old_name, &sb, NULL, 0);
 +              strbuf_addstr(&sb, " => ");
 +              quote_c_style(patch->new_name, &sb, NULL, 0);
        } else {
                const char *n = patch->new_name;
                if (!n)
                        n = patch->old_name;
 -              quote_c_style(n, NULL, output, 0);
 +              quote_c_style(n, &sb, NULL, 0);
        }
 -      fputs(post, output);
 +      fprintf(output, fmt, sb.buf);
 +      fputc('\n', output);
 +      strbuf_release(&sb);
  }
  
 -#define CHUNKSIZE (8192)
  #define SLOP (16)
  
  static void read_patch_file(struct strbuf *sb, int fd)
@@@ -467,7 -415,7 +467,7 @@@ static char *squash_slash(char *name
        return name;
  }
  
 -static char *find_name_gnu(const char *line, char *def, int p_value)
 +static char *find_name_gnu(const char *line, const char *def, int p_value)
  {
        struct strbuf name = STRBUF_INIT;
        char *cp;
                cp++;
        }
  
 -      /* name can later be freed, so we need
 -       * to memmove, not just return cp
 -       */
        strbuf_remove(&name, 0, cp - name.buf);
 -      free(def);
        if (root)
                strbuf_insert(&name, 0, root, root_len);
        return squash_slash(strbuf_detach(&name, NULL));
@@@ -655,13 -607,8 +655,13 @@@ static size_t diff_timestamp_len(const 
        return line + len - end;
  }
  
 -static char *find_name_common(const char *line, char *def, int p_value,
 -                              const char *end, int terminate)
 +static char *null_strdup(const char *s)
 +{
 +      return s ? xstrdup(s) : NULL;
 +}
 +
 +static char *find_name_common(const char *line, const char *def,
 +                            int p_value, const char *end, int terminate)
  {
        int len;
        const char *start = NULL;
                        start = line;
        }
        if (!start)
 -              return squash_slash(def);
 +              return squash_slash(null_strdup(def));
        len = line - start;
        if (!len)
 -              return squash_slash(def);
 +              return squash_slash(null_strdup(def));
  
        /*
         * Generally we prefer the shorter name, especially
        if (def) {
                int deflen = strlen(def);
                if (deflen < len && !strncmp(start, def, deflen))
 -                      return squash_slash(def);
 -              free(def);
 +                      return squash_slash(xstrdup(def));
        }
  
        if (root) {
@@@ -821,7 -769,7 +821,7 @@@ static int has_epoch_timestamp(const ch
        if (!stamp) {
                stamp = xmalloc(sizeof(*stamp));
                if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
 -                      warning("Cannot prepare timestamp regexp %s",
 +                      warning(_("Cannot prepare timestamp regexp %s"),
                                stamp_regexp);
                        return 0;
                }
        status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
        if (status) {
                if (status != REG_NOMATCH)
 -                      warning("regexec returned %d for input: %s",
 +                      warning(_("regexec returned %d for input: %s"),
                                status, timestamp);
                return 0;
        }
@@@ -893,10 -841,8 +893,10 @@@ static void parse_traditional_patch(con
                name = find_name_traditional(first, NULL, p_value);
                patch->old_name = name;
        } else {
 -              name = find_name_traditional(first, NULL, p_value);
 -              name = find_name_traditional(second, name, p_value);
 +              char *first_name;
 +              first_name = find_name_traditional(first, NULL, p_value);
 +              name = find_name_traditional(second, first_name, p_value);
 +              free(first_name);
                if (has_epoch_timestamp(first)) {
                        patch->is_new = 1;
                        patch->is_delete = 0;
                        patch->is_delete = 1;
                        patch->old_name = name;
                } else {
 -                      patch->old_name = patch->new_name = name;
 +                      patch->old_name = name;
 +                      patch->new_name = xstrdup(name);
                }
        }
        if (!name)
 -              die("unable to find filename in patch at line %d", linenr);
 +              die(_("unable to find filename in patch at line %d"), linenr);
  }
  
  static int gitdiff_hdrend(const char *line, struct patch *patch)
   * 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)
 +#define DIFF_OLD_NAME 0
 +#define DIFF_NEW_NAME 1
 +
 +static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, int side)
  {
        if (!orig_name && !isnull)
                return find_name(line, NULL, p_value, TERM_TAB);
                name = orig_name;
                len = strlen(name);
                if (isnull)
 -                      die("git apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
 +                      die(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"), name, linenr);
                another = find_name(line, NULL, p_value, TERM_TAB);
                if (!another || memcmp(another, name, len + 1))
 -                      die("git apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
 +                      die((side == DIFF_NEW_NAME) ?
 +                          _("git apply: bad git-diff - inconsistent new filename on line %d") :
 +                          _("git apply: bad git-diff - inconsistent old filename on line %d"), 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);
 +                      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");
 +      char *orig = patch->old_name;
 +      patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name,
 +                                            DIFF_OLD_NAME);
 +      if (orig != patch->old_name)
 +              free(orig);
        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");
 +      char *orig = patch->new_name;
 +      patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name,
 +                                            DIFF_NEW_NAME);
 +      if (orig != patch->new_name)
 +              free(orig);
        return 0;
  }
  
@@@ -995,23 -927,20 +995,23 @@@ static int gitdiff_newmode(const char *
  static int gitdiff_delete(const char *line, struct patch *patch)
  {
        patch->is_delete = 1;
 -      patch->old_name = patch->def_name;
 +      free(patch->old_name);
 +      patch->old_name = null_strdup(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;
 +      free(patch->new_name);
 +      patch->new_name = null_strdup(patch->def_name);
        return gitdiff_newmode(line, patch);
  }
  
  static int gitdiff_copysrc(const char *line, struct patch *patch)
  {
        patch->is_copy = 1;
 +      free(patch->old_name);
        patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
  static int gitdiff_copydst(const char *line, struct patch *patch)
  {
        patch->is_copy = 1;
 +      free(patch->new_name);
        patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
  static int gitdiff_renamesrc(const char *line, struct patch *patch)
  {
        patch->is_rename = 1;
 +      free(patch->old_name);
        patch->old_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
  static int gitdiff_renamedst(const char *line, struct patch *patch)
  {
        patch->is_rename = 1;
 +      free(patch->new_name);
        patch->new_name = find_name(line, NULL, p_value ? p_value - 1 : 0, 0);
        return 0;
  }
@@@ -1117,7 -1043,7 +1117,7 @@@ static const char *stop_at_slash(const 
   * 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)
 +static char *git_header_name(const char *line, int llen)
  {
        const char *name;
        const char *second = 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)
 +static int parse_git_header(const char *line, int len, unsigned int size, struct patch *patch)
  {
        unsigned long offset;
  
@@@ -1360,7 -1286,7 +1360,7 @@@ static int parse_range(const char *line
        return offset + ex;
  }
  
 -static void recount_diff(char *line, int size, struct fragment *fragment)
 +static void recount_diff(const char *line, int size, struct fragment *fragment)
  {
        int oldlines = 0, newlines = 0, ret = 0;
  
                        break;
                }
                if (ret) {
 -                      warning("recount: unexpected line: %.*s",
 +                      warning(_("recount: unexpected line: %.*s"),
                                (int)linelen(line, size), line);
                        return;
                }
   * 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)
 +static int parse_fragment_header(const char *line, int len, struct fragment *fragment)
  {
        int offset;
  
        return offset;
  }
  
 -static int find_header(char *line, unsigned long size, int *hdrsize, struct patch *patch)
 +static int find_header(const char *line, unsigned long size, int *hdrsize, struct patch *patch)
  {
        unsigned long offset, len;
  
                        struct fragment dummy;
                        if (parse_fragment_header(line, len, &dummy) < 0)
                                continue;
 -                      die("patch fragment without header at line %d: %.*s",
 +                      die(_("patch fragment without header at line %d: %.*s"),
                            linenr, (int)len-1, line);
                }
  
                                continue;
                        if (!patch->old_name && !patch->new_name) {
                                if (!patch->def_name)
 -                                      die("git diff header lacks filename information when removing "
 -                                          "%d leading pathname components (line %d)" , p_value, linenr);
 -                              patch->old_name = patch->new_name = patch->def_name;
 +                                      die(Q_("git diff header lacks filename information when removing "
 +                                             "%d leading pathname component (line %d)",
 +                                             "git diff header lacks filename information when removing "
 +                                             "%d leading pathname components (line %d)",
 +                                             p_value),
 +                                          p_value, linenr);
 +                              patch->old_name = xstrdup(patch->def_name);
 +                              patch->new_name = xstrdup(patch->def_name);
                        }
                        if (!patch->is_delete && !patch->new_name)
                                die("git diff header lacks filename information "
@@@ -1544,7 -1465,7 +1544,7 @@@ static void check_whitespace(const cha
   * 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,
 +static int parse_fragment(const char *line, unsigned long size,
                          struct patch *patch, struct fragment *fragment)
  {
        int added, deleted;
        patch->lines_deleted += deleted;
  
        if (0 < patch->is_new && oldlines)
 -              return error("new file depends on old contents");
 +              return error(_("new file depends on old contents"));
        if (0 < patch->is_delete && newlines)
 -              return error("deleted file still has contents");
 +              return error(_("deleted file still has contents"));
        return offset;
  }
  
 -static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
 +/*
 + * We have seen "diff --git a/... b/..." header (or a traditional patch
 + * header).  Read hunks that belong to this patch into fragments and hang
 + * them to the given patch structure.
 + *
 + * The (fragment->patch, fragment->size) pair points into the memory given
 + * by the caller, not a copy, when we return.
 + */
 +static int parse_single_patch(const char *line, unsigned long size, struct patch *patch)
  {
        unsigned long offset = 0;
        unsigned long oldlines = 0, newlines = 0, context = 0;
                fragment->linenr = linenr;
                len = parse_fragment(line, size, patch, fragment);
                if (len <= 0)
 -                      die("corrupt patch at line %d", linenr);
 +                      die(_("corrupt patch at line %d"), linenr);
                fragment->patch = line;
                fragment->size = len;
                oldlines += fragment->oldlines;
                patch->is_delete = 0;
  
        if (0 < patch->is_new && oldlines)
 -              die("new file %s depends on old contents", patch->new_name);
 +              die(_("new file %s depends on old contents"), patch->new_name);
        if (0 < patch->is_delete && newlines)
 -              die("deleted file %s still has contents", patch->old_name);
 +              die(_("deleted file %s still has contents"), patch->old_name);
        if (!patch->is_delete && !newlines && context)
 -              fprintf(stderr, "** warning: file %s becomes empty but "
 -                      "is not deleted\n", patch->new_name);
 +              fprintf_ln(stderr,
 +                         _("** warning: "
 +                           "file %s becomes empty but is not deleted"),
 +                         patch->new_name);
  
        return offset;
  }
@@@ -1743,11 -1654,6 +1743,11 @@@ static char *inflate_it(const void *dat
        return out;
  }
  
 +/*
 + * Read a binary hunk and return a new fragment; fragment->patch
 + * points at an allocated memory that the caller must free, so
 + * it is marked as "->free_patch = 1".
 + */
  static struct fragment *parse_binary_hunk(char **buf_p,
                                          unsigned long *sz_p,
                                          int *status_p,
  
        frag = xcalloc(1, sizeof(*frag));
        frag->patch = inflate_it(data, hunk_size, origlen);
 +      frag->free_patch = 1;
        if (!frag->patch)
                goto corrupt;
        free(data);
   corrupt:
        free(data);
        *status_p = -1;
 -      error("corrupt binary patch at line %d: %.*s",
 +      error(_("corrupt binary patch at line %d: %.*s"),
              linenr-1, llen-1, buffer);
        return NULL;
  }
@@@ -1878,7 -1783,7 +1878,7 @@@ static int parse_binary(char *buffer, u
        forward = parse_binary_hunk(&buffer, &size, &status, &used);
        if (!forward && !status)
                /* there has to be one hunk (forward hunk) */
 -              return error("unrecognized binary patch at line %d", linenr-1);
 +              return error(_("unrecognized binary patch at line %d"), linenr-1);
        if (status)
                /* otherwise we already gave an error message */
                return status;
        return used;
  }
  
 +/*
 + * Read the patch text in "buffer" taht extends for "size" bytes; stop
 + * reading after seeing a single patch (i.e. changes to a single file).
 + * Create fragments (i.e. patch hunks) and hang them to the given patch.
 + * Return the number of bytes consumed, so that the caller can call us
 + * again for the next patch.
 + */
  static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
  {
        int hdrsize, patchsize;
                 */
                if ((apply || check) &&
                    (!patch->is_binary && !metadata_changes(patch)))
 -                      die("patch with only garbage at line %d", linenr);
 +                      die(_("patch with only garbage at line %d"), linenr);
        }
  
        return offset + hdrsize + patchsize;
@@@ -2054,11 -1952,11 +2054,11 @@@ static int read_old_data(struct stat *s
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
 -                      return error("unable to read symlink %s", path);
 +                      return error(_("unable to read symlink %s"), path);
                return 0;
        case S_IFREG:
                if (strbuf_read_file(buf, path, st->st_size) != st->st_size)
 -                      return error("unable to open or read %s", path);
 +                      return error(_("unable to open or read %s"), path);
                convert_to_git(path, buf->buf, buf->len, buf, 0);
                return 0;
        default:
@@@ -2129,7 -2027,7 +2129,7 @@@ static void update_pre_post_images(stru
                        ctx++;
                }
                if (preimage->nr <= ctx)
 -                      die("oops");
 +                      die(_("oops"));
  
                /* and copy it in, while fixing the line length */
                len = preimage->line[ctx].len;
@@@ -2468,11 -2366,6 +2468,11 @@@ static void remove_last_line(struct ima
        img->len -= img->line[--img->nr].len;
  }
  
 +/*
 + * The change from "preimage" and "postimage" has been found to
 + * apply at applied_pos (counts in line numbers) in "img".
 + * Update "img" to remove "preimage" and replace it with "postimage".
 + */
  static void update_image(struct image *img,
                         int applied_pos,
                         struct image *preimage,
        img->nr = nr;
  }
  
 +/*
 + * Use the patch-hunk text in "frag" to prepare two images (preimage and
 + * postimage) for the hunk.  Find lines that match "preimage" in "img" and
 + * replace the part of "img" with "postimage" text.
 + */
  static int apply_one_fragment(struct image *img, struct fragment *frag,
                              int inaccurate_eof, unsigned ws_rule,
                              int nth_fragment)
                        break;
                default:
                        if (apply_verbosely)
 -                              error("invalid start of line: '%c'", first);
 +                              error(_("invalid start of line: '%c'"), first);
                        return -1;
                }
                if (added_blank_line) {
                        int offset = applied_pos - pos;
                        if (apply_in_reverse)
                                offset = 0 - offset;
 -                      fprintf(stderr,
 -                              "Hunk #%d succeeded at %d (offset %d lines).\n",
 -                              nth_fragment, applied_pos + 1, offset);
 +                      fprintf_ln(stderr,
 +                                 Q_("Hunk #%d succeeded at %d (offset %d line).",
 +                                    "Hunk #%d succeeded at %d (offset %d lines).",
 +                                    offset),
 +                                 nth_fragment, applied_pos + 1, offset);
                }
  
                /*
                 */
                if ((leading != frag->leading) ||
                    (trailing != frag->trailing))
 -                      fprintf(stderr, "Context reduced to (%ld/%ld)"
 -                              " to apply fragment at %d\n",
 -                              leading, trailing, applied_pos+1);
 +                      fprintf_ln(stderr, _("Context reduced to (%ld/%ld)"
 +                                           " to apply fragment at %d"),
 +                                 leading, trailing, applied_pos+1);
                update_image(img, applied_pos, &preimage, &postimage);
        } else {
                if (apply_verbosely)
 -                      error("while searching for:\n%.*s",
 +                      error(_("while searching for:\n%.*s"),
                              (int)(old - oldlines), oldlines);
        }
  
@@@ -2806,7 -2692,7 +2806,7 @@@ static int apply_binary_fragment(struc
        void *dst;
  
        if (!fragment)
 -              return error("missing binary patch data for '%s'",
 +              return error(_("missing binary patch data for '%s'"),
                             patch->new_name ?
                             patch->new_name :
                             patch->old_name);
        return -1;
  }
  
 +/*
 + * Replace "img" with the result of applying the binary patch.
 + * The binary patch data itself in patch->fragment is still kept
 + * but the preimage prepared by the caller in "img" is freed here
 + * or in the helper function apply_binary_fragment() this calls.
 + */
  static int apply_binary(struct image *img, struct patch *patch)
  {
        const char *name = patch->old_name ? patch->old_name : patch->new_name;
                 * in the patch->fragments->{patch,size}.
                 */
                if (apply_binary_fragment(img, patch))
 -                      return error("binary patch does not apply to '%s'",
 +                      return error(_("binary patch does not apply to '%s'"),
                                     name);
  
                /* verify that the result matches */
                hash_sha1_file(img->buf, img->len, blob_type, sha1);
                if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
 -                      return error("binary patch to '%s' creates incorrect result (expecting %s, got %s)",
 +                      return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
                                name, patch->new_sha1_prefix, sha1_to_hex(sha1));
        }
  
@@@ -2936,7 -2816,7 +2936,7 @@@ static int apply_fragments(struct imag
        while (frag) {
                nth++;
                if (apply_one_fragment(img, frag, inaccurate_eof, ws_rule, nth)) {
 -                      error("patch failed: %s:%ld", name, frag->oldpos);
 +                      error(_("patch failed: %s:%ld"), name, frag->oldpos);
                        if (!apply_with_reject)
                                return -1;
                        frag->rejected = 1;
        return 0;
  }
  
 -static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
 +static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode)
  {
 -      if (!ce)
 -              return 0;
 -
 -      if (S_ISGITLINK(ce->ce_mode)) {
 +      if (S_ISGITLINK(mode)) {
                strbuf_grow(buf, 100);
 -              strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
 +              strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1));
        } else {
                enum object_type type;
                unsigned long sz;
                char *result;
  
 -              result = read_sha1_file(ce->sha1, &type, &sz);
 +              result = read_sha1_file(sha1, &type, &sz);
                if (!result)
                        return -1;
                /* XXX read_sha1_file NUL-terminates */
        return 0;
  }
  
 +static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
 +{
 +      if (!ce)
 +              return 0;
 +      return read_blob_object(buf, ce->sha1, ce->ce_mode);
 +}
 +
  static struct patch *in_fn_table(const char *name)
  {
        struct string_list_item *item;
   * item->util in the filename table records the status of the path.
   * Usually it points at a patch (whose result records the contents
   * of it after applying it), but it could be PATH_WAS_DELETED for a
 - * path that a previously applied patch has already removed.
 + * path that a previously applied patch has already removed, or
 + * PATH_TO_BE_DELETED for a path that a later patch would remove.
 + *
 + * The latter is needed to deal with a case where two paths A and B
 + * are swapped by first renaming A to B and then renaming B to A;
 + * moving A to B should not be prevented due to presense of B as we
 + * will remove it in a later patch.
   */
 - #define PATH_TO_BE_DELETED ((struct patch *) -2)
 +#define PATH_TO_BE_DELETED ((struct patch *) -2)
  #define PATH_WAS_DELETED ((struct patch *) -1)
  
  static int to_be_deleted(struct patch *patch)
@@@ -3050,346 -2920,152 +3050,346 @@@ static void prepare_fn_table(struct pat
        }
  }
  
 -static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 +static int checkout_target(struct cache_entry *ce, struct stat *st)
 +{
 +      struct checkout costate;
 +
 +      memset(&costate, 0, sizeof(costate));
 +      costate.base_dir = "";
 +      costate.refresh_cache = 1;
 +      if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
 +              return error(_("cannot checkout %s"), ce->name);
 +      return 0;
 +}
 +
 +static struct patch *previous_patch(struct patch *patch, int *gone)
 +{
 +      struct patch *previous;
 +
 +      *gone = 0;
 +      if (patch->is_copy || patch->is_rename)
 +              return NULL; /* "git" patches do not depend on the order */
 +
 +      previous = in_fn_table(patch->old_name);
 +      if (!previous)
 +              return NULL;
 +
 +      if (to_be_deleted(previous))
 +              return NULL; /* the deletion hasn't happened yet */
 +
 +      if (was_deleted(previous))
 +              *gone = 1;
 +
 +      return previous;
 +}
 +
 +static int verify_index_match(struct cache_entry *ce, struct stat *st)
 +{
 +      if (S_ISGITLINK(ce->ce_mode)) {
 +              if (!S_ISDIR(st->st_mode))
 +                      return -1;
 +              return 0;
 +      }
 +      return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 +}
 +
 +#define SUBMODULE_PATCH_WITHOUT_INDEX 1
 +
 +static int load_patch_target(struct strbuf *buf,
 +                           struct cache_entry *ce,
 +                           struct stat *st,
 +                           const char *name,
 +                           unsigned expected_mode)
 +{
 +      if (cached) {
 +              if (read_file_or_gitlink(ce, buf))
 +                      return error(_("read of %s failed"), name);
 +      } else if (name) {
 +              if (S_ISGITLINK(expected_mode)) {
 +                      if (ce)
 +                              return read_file_or_gitlink(ce, buf);
 +                      else
 +                              return SUBMODULE_PATCH_WITHOUT_INDEX;
 +              } else {
 +                      if (read_old_data(st, name, buf))
 +                              return error(_("read of %s failed"), name);
 +              }
 +      }
 +      return 0;
 +}
 +
 +/*
 + * We are about to apply "patch"; populate the "image" with the
 + * current version we have, from the working tree or from the index,
 + * depending on the situation e.g. --cached/--index.  If we are
 + * applying a non-git patch that incrementally updates the tree,
 + * we read from the result of a previous diff.
 + */
 +static int load_preimage(struct image *image,
 +                       struct patch *patch, struct stat *st, struct cache_entry *ce)
  {
        struct strbuf buf = STRBUF_INIT;
 -      struct image image;
        size_t len;
        char *img;
 -      struct patch *tpatch;
 +      struct patch *previous;
 +      int status;
  
 -      if (!(patch->is_copy || patch->is_rename) &&
 -          (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
 -              if (was_deleted(tpatch)) {
 -                      return error("patch %s has been renamed/deleted",
 -                              patch->old_name);
 -              }
 -              /* We have a patched copy in memory use that */
 -              strbuf_add(&buf, tpatch->result, tpatch->resultsize);
 -      } else if (cached) {
 -              if (read_file_or_gitlink(ce, &buf))
 -                      return error("read of %s failed", patch->old_name);
 -      } else if (patch->old_name) {
 -              if (S_ISGITLINK(patch->old_mode)) {
 -                      if (ce) {
 -                              read_file_or_gitlink(ce, &buf);
 -                      } else {
 -                              /*
 -                               * There is no way to apply subproject
 -                               * patch without looking at the index.
 -                               */
 -                              patch->fragments = NULL;
 -                      }
 -              } else {
 -                      if (read_old_data(st, patch->old_name, &buf))
 -                              return error("read of %s failed", patch->old_name);
 +      previous = previous_patch(patch, &status);
 +      if (status)
 +              return error(_("path %s has been renamed/deleted"),
 +                           patch->old_name);
 +      if (previous) {
 +              /* We have a patched copy in memory; use that. */
 +              strbuf_add(&buf, previous->result, previous->resultsize);
 +      } else {
 +              status = load_patch_target(&buf, ce, st,
 +                                         patch->old_name, patch->old_mode);
 +              if (status < 0)
 +                      return status;
 +              else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) {
 +                      /*
 +                       * There is no way to apply subproject
 +                       * patch without looking at the index.
 +                       * NEEDSWORK: shouldn't this be flagged
 +                       * as an error???
 +                       */
 +                      free_fragment_list(patch->fragments);
 +                      patch->fragments = NULL;
 +              } else if (status) {
 +                      return error(_("read of %s failed"), patch->old_name);
                }
        }
  
        img = strbuf_detach(&buf, &len);
 -      prepare_image(&image, img, len, !patch->is_binary);
 +      prepare_image(image, img, len, !patch->is_binary);
 +      return 0;
 +}
  
 -      if (apply_fragments(&image, patch) < 0)
 -              return -1; /* note with --reject this succeeds. */
 -      patch->result = image.buf;
 -      patch->resultsize = image.len;
 -      add_to_fn_table(patch);
 -      free(image.line_allocated);
 +static int three_way_merge(struct image *image,
 +                         char *path,
 +                         const unsigned char *base,
 +                         const unsigned char *ours,
 +                         const unsigned char *theirs)
 +{
 +      mmfile_t base_file, our_file, their_file;
 +      mmbuffer_t result = { NULL };
 +      int status;
  
 -      if (0 < patch->is_delete && patch->resultsize)
 -              return error("removal patch leaves file contents");
 +      read_mmblob(&base_file, base);
 +      read_mmblob(&our_file, ours);
 +      read_mmblob(&their_file, theirs);
 +      status = ll_merge(&result, path,
 +                        &base_file, "base",
 +                        &our_file, "ours",
 +                        &their_file, "theirs", NULL);
 +      free(base_file.ptr);
 +      free(our_file.ptr);
 +      free(their_file.ptr);
 +      if (status < 0 || !result.ptr) {
 +              free(result.ptr);
 +              return -1;
 +      }
 +      clear_image(image);
 +      image->buf = result.ptr;
 +      image->len = result.size;
 +
 +      return status;
 +}
 +
 +/*
 + * When directly falling back to add/add three-way merge, we read from
 + * the current contents of the new_name.  In no cases other than that
 + * this function will be called.
 + */
 +static int load_current(struct image *image, struct patch *patch)
 +{
 +      struct strbuf buf = STRBUF_INIT;
 +      int status, pos;
 +      size_t len;
 +      char *img;
 +      struct stat st;
 +      struct cache_entry *ce;
 +      char *name = patch->new_name;
 +      unsigned mode = patch->new_mode;
 +
 +      if (!patch->is_new)
 +              die("BUG: patch to %s is not a creation", patch->old_name);
 +
 +      pos = cache_name_pos(name, strlen(name));
 +      if (pos < 0)
 +              return error(_("%s: does not exist in index"), name);
 +      ce = active_cache[pos];
 +      if (lstat(name, &st)) {
 +              if (errno != ENOENT)
 +                      return error(_("%s: %s"), name, strerror(errno));
 +              if (checkout_target(ce, &st))
 +                      return -1;
 +      }
 +      if (verify_index_match(ce, &st))
 +              return error(_("%s: does not match index"), name);
  
 +      status = load_patch_target(&buf, ce, &st, name, mode);
 +      if (status < 0)
 +              return status;
 +      else if (status)
 +              return -1;
 +      img = strbuf_detach(&buf, &len);
 +      prepare_image(image, img, len, !patch->is_binary);
        return 0;
  }
  
 -static int check_to_create_blob(const char *new_name, int ok_if_exists)
 +static int try_threeway(struct image *image, struct patch *patch,
 +                      struct stat *st, struct cache_entry *ce)
  {
 -      struct stat nst;
 -      if (!lstat(new_name, &nst)) {
 -              if (S_ISDIR(nst.st_mode) || ok_if_exists)
 -                      return 0;
 -              /*
 -               * A leading component of new_name might be a symlink
 -               * that is going to be removed with this patch, but
 -               * still pointing at somewhere that has the path.
 -               * In such a case, path "new_name" does not exist as
 -               * far as git is concerned.
 -               */
 -              if (has_symlink_leading_path(new_name, strlen(new_name)))
 -                      return 0;
 +      unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
 +      struct strbuf buf = STRBUF_INIT;
 +      size_t len;
 +      int status;
 +      char *img;
 +      struct image tmp_image;
 +
 +      /* No point falling back to 3-way merge in these cases */
 +      if (patch->is_delete ||
 +          S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))
 +              return -1;
 +
 +      /* Preimage the patch was prepared for */
 +      if (patch->is_new)
 +              write_sha1_file("", 0, blob_type, pre_sha1);
 +      else if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
 +               read_blob_object(&buf, pre_sha1, patch->old_mode))
 +              return error("repository lacks the necessary blob to fall back on 3-way merge.");
 +
 +      fprintf(stderr, "Falling back to three-way merge...\n");
  
 -              return error("%s: already exists in working directory", new_name);
 +      img = strbuf_detach(&buf, &len);
 +      prepare_image(&tmp_image, img, len, 1);
 +      /* Apply the patch to get the post image */
 +      if (apply_fragments(&tmp_image, patch) < 0) {
 +              clear_image(&tmp_image);
 +              return -1;
 +      }
 +      /* post_sha1[] is theirs */
 +      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1);
 +      clear_image(&tmp_image);
 +
 +      /* our_sha1[] is ours */
 +      if (patch->is_new) {
 +              if (load_current(&tmp_image, patch))
 +                      return error("cannot read the current contents of '%s'",
 +                                   patch->new_name);
 +      } else {
 +              if (load_preimage(&tmp_image, patch, st, ce))
 +                      return error("cannot read the current contents of '%s'",
 +                                   patch->old_name);
 +      }
 +      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);
 +      clear_image(&tmp_image);
 +
 +      /* in-core three-way merge between post and our using pre as base */
 +      status = three_way_merge(image, patch->new_name,
 +                               pre_sha1, our_sha1, post_sha1);
 +      if (status < 0) {
 +              fprintf(stderr, "Failed to fall back on three-way merge...\n");
 +              return status;
 +      }
 +
 +      if (status) {
 +              patch->conflicted_threeway = 1;
 +              if (patch->is_new)
 +                      hashclr(patch->threeway_stage[0]);
 +              else
 +                      hashcpy(patch->threeway_stage[0], pre_sha1);
 +              hashcpy(patch->threeway_stage[1], our_sha1);
 +              hashcpy(patch->threeway_stage[2], post_sha1);
 +              fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
 +      } else {
 +              fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
        }
 -      else if ((errno != ENOENT) && (errno != ENOTDIR))
 -              return error("%s: %s", new_name, strerror(errno));
        return 0;
  }
  
 -static int verify_index_match(struct cache_entry *ce, struct stat *st)
 +static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
  {
 -      if (S_ISGITLINK(ce->ce_mode)) {
 -              if (!S_ISDIR(st->st_mode))
 +      struct image image;
 +
 +      if (load_preimage(&image, patch, st, ce) < 0)
 +              return -1;
 +
 +      if (patch->direct_to_threeway ||
 +          apply_fragments(&image, patch) < 0) {
 +              /* Note: with --reject, apply_fragments() returns 0 */
 +              if (!threeway || try_threeway(&image, patch, st, ce) < 0)
                        return -1;
 -              return 0;
        }
 -      return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 +      patch->result = image.buf;
 +      patch->resultsize = image.len;
 +      add_to_fn_table(patch);
 +      free(image.line_allocated);
 +
 +      if (0 < patch->is_delete && patch->resultsize)
 +              return error(_("removal patch leaves file contents"));
 +
 +      return 0;
  }
  
 +/*
 + * If "patch" that we are looking at modifies or deletes what we have,
 + * we would want it not to lose any local modification we have, either
 + * in the working tree or in the index.
 + *
 + * This also decides if a non-git patch is a creation patch or a
 + * modification to an existing empty file.  We do not check the state
 + * of the current tree for a creation patch in this function; the caller
 + * check_patch() separately makes sure (and errors out otherwise) that
 + * the path the patch creates does not exist in the current tree.
 + */
  static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
  {
        const char *old_name = patch->old_name;
 -      struct patch *tpatch = NULL;
 -      int stat_ret = 0;
 +      struct patch *previous = NULL;
 +      int stat_ret = 0, status;
        unsigned st_mode = 0;
  
 -      /*
 -       * Make sure that we do not have local modifications from the
 -       * index when we are looking at the index.  Also make sure
 -       * we have the preimage file to be patched in the work tree,
 -       * unless --cached, which tells git to apply only in the index.
 -       */
        if (!old_name)
                return 0;
  
        assert(patch->is_new <= 0);
 +      previous = previous_patch(patch, &status);
  
 -      if (!(patch->is_copy || patch->is_rename) &&
 -          (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
 -              if (was_deleted(tpatch))
 -                      return error("%s: has been deleted/renamed", old_name);
 -              st_mode = tpatch->new_mode;
 +      if (status)
 +              return error(_("path %s has been renamed/deleted"), old_name);
 +      if (previous) {
 +              st_mode = previous->new_mode;
        } else if (!cached) {
                stat_ret = lstat(old_name, st);
                if (stat_ret && errno != ENOENT)
 -                      return error("%s: %s", old_name, strerror(errno));
 +                      return error(_("%s: %s"), old_name, strerror(errno));
        }
  
 -      if (to_be_deleted(tpatch))
 -              tpatch = NULL;
 -
 -      if (check_index && !tpatch) {
 +      if (check_index && !previous) {
                int pos = cache_name_pos(old_name, strlen(old_name));
                if (pos < 0) {
                        if (patch->is_new < 0)
                                goto is_new;
 -                      return error("%s: does not exist in index", old_name);
 +                      return error(_("%s: does not exist in index"), old_name);
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
 -                      struct checkout costate;
 -                      /* checkout */
 -                      memset(&costate, 0, sizeof(costate));
 -                      costate.base_dir = "";
 -                      costate.refresh_cache = 1;
 -                      if (checkout_entry(*ce, &costate, NULL) ||
 -                          lstat(old_name, st))
 +                      if (checkout_target(*ce, st))
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
 -                      return error("%s: does not match index", old_name);
 +                      return error(_("%s: does not match index"), old_name);
                if (cached)
                        st_mode = (*ce)->ce_mode;
        } else if (stat_ret < 0) {
                if (patch->is_new < 0)
                        goto is_new;
 -              return error("%s: %s", old_name, strerror(errno));
 +              return error(_("%s: %s"), old_name, strerror(errno));
        }
  
 -      if (!cached && !tpatch)
 +      if (!cached && !previous)
                st_mode = ce_mode_from_stat(*ce, st->st_mode);
  
        if (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);
 +              return error(_("%s: wrong type"), old_name);
        if (st_mode != patch->old_mode)
 -              warning("%s has type %o, expected %o",
 +              warning(_("%s has type %o, expected %o"),
                        old_name, st_mode, patch->old_mode);
        if (!patch->new_mode && !patch->is_delete)
                patch->new_mode = st_mode;
   is_new:
        patch->is_new = 1;
        patch->is_delete = 0;
 +      free(patch->old_name);
        patch->old_name = NULL;
        return 0;
  }
  
 +
 +#define EXISTS_IN_INDEX 1
 +#define EXISTS_IN_WORKTREE 2
 +
 +static int check_to_create(const char *new_name, int ok_if_exists)
 +{
 +      struct stat nst;
 +
 +      if (check_index &&
 +          cache_name_pos(new_name, strlen(new_name)) >= 0 &&
 +          !ok_if_exists)
 +              return EXISTS_IN_INDEX;
 +      if (cached)
 +              return 0;
 +
 +      if (!lstat(new_name, &nst)) {
 +              if (S_ISDIR(nst.st_mode) || ok_if_exists)
 +                      return 0;
 +              /*
 +               * A leading component of new_name might be a symlink
 +               * that is going to be removed with this patch, but
 +               * still pointing at somewhere that has the path.
 +               * In such a case, path "new_name" does not exist as
 +               * far as git is concerned.
 +               */
 +              if (has_symlink_leading_path(new_name, strlen(new_name)))
 +                      return 0;
 +
 +              return EXISTS_IN_WORKTREE;
 +      } else if ((errno != ENOENT) && (errno != ENOTDIR)) {
 +              return error("%s: %s", new_name, strerror(errno));
 +      }
 +      return 0;
 +}
 +
 +/*
 + * Check and apply the patch in-core; leave the result in patch->result
 + * for the caller to write it out to the final destination.
 + */
  static int check_patch(struct patch *patch)
  {
        struct stat st;
                return status;
        old_name = patch->old_name;
  
 +      /*
 +       * A type-change diff is always split into a patch to delete
 +       * old, immediately followed by a patch to create new (see
 +       * diff.c::run_diff()); in such a case it is Ok that the entry
 +       * to be deleted by the previous patch is still in the working
 +       * tree and in the index.
 +       *
 +       * A patch to swap-rename between A and B would first rename A
 +       * to B and then rename B to A.  While applying the first one,
 +       * the presense of B should not stop A from getting renamed to
 +       * B; ask to_be_deleted() about the later rename.  Removal of
 +       * B and rename from A to B is handled the same way by asking
 +       * was_deleted().
 +       */
        if ((tpatch = in_fn_table(new_name)) &&
 -                      (was_deleted(tpatch) || to_be_deleted(tpatch)))
 -              /*
 -               * A type-change diff is always split into a patch to
 -               * delete old, immediately followed by a patch to
 -               * create new (see diff.c::run_diff()); in such a case
 -               * it is Ok that the entry to be deleted by the
 -               * previous patch is still in the working tree and in
 -               * the index.
 -               */
 +          (was_deleted(tpatch) || to_be_deleted(tpatch)))
                ok_if_exists = 1;
        else
                ok_if_exists = 0;
  
        if (new_name &&
            ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
 -              if (check_index &&
 -                  cache_name_pos(new_name, strlen(new_name)) >= 0 &&
 -                  !ok_if_exists)
 -                      return error("%s: already exists in index", new_name);
 -              if (!cached) {
 -                      int err = check_to_create_blob(new_name, ok_if_exists);
 -                      if (err)
 -                              return err;
 +              int err = check_to_create(new_name, ok_if_exists);
 +
 +              if (err && threeway) {
 +                      patch->direct_to_threeway = 1;
 +              } else switch (err) {
 +              case 0:
 +                      break; /* happy */
 +              case EXISTS_IN_INDEX:
 +                      return error(_("%s: already exists in index"), new_name);
 +                      break;
 +              case EXISTS_IN_WORKTREE:
 +                      return error(_("%s: already exists in working directory"),
 +                                   new_name);
 +              default:
 +                      return err;
                }
 +
                if (!patch->new_mode) {
                        if (0 < patch->is_new)
                                patch->new_mode = S_IFREG | 0644;
                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 ((patch->old_mode ^ patch->new_mode) & S_IFMT) {
 +                      if (same)
 +                              return error(_("new mode (%o) of %s does not "
 +                                             "match old mode (%o)"),
 +                                      patch->new_mode, new_name,
 +                                      patch->old_mode);
 +                      else
 +                              return error(_("new mode (%o) of %s does not "
 +                                             "match old mode (%o) of %s"),
 +                                      patch->new_mode, new_name,
 +                                      patch->old_mode, old_name);
 +              }
        }
  
        if (apply_data(patch, &st, ce) < 0)
 -              return error("%s: patch does not apply", name);
 +              return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
        return 0;
  }
@@@ -3549,7 -3163,7 +3549,7 @@@ static int check_patch_list(struct patc
        while (patch) {
                if (apply_verbosely)
                        say_patch_name(stderr,
 -                                     "Checking patch ", patch, "...\n");
 +                                     _("Checking patch %s..."), patch);
                err |= check_patch(patch);
                patch = patch->next;
        }
@@@ -3589,7 -3203,7 +3589,7 @@@ static void build_fake_ancestor(struct 
                name = patch->old_name ? patch->old_name : patch->new_name;
                if (0 < patch->is_new)
                        continue;
-               else if (get_sha1(patch->old_sha1_prefix, sha1))
+               else if (get_sha1_blob(patch->old_sha1_prefix, sha1))
                        /* git diff has no index line for mode/type changes */
                        if (!patch->lines_added && !patch->lines_deleted) {
                                if (get_current_sha1(patch->old_name, sha1))
  
                ce = make_cache_entry(patch->old_mode, sha1_ptr, name, 0, 0);
                if (!ce)
 -                      die("make_cache_entry failed for path '%s'", name);
 +                      die(_("make_cache_entry failed for path '%s'"), name);
                if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD))
                        die ("Could not add %s to temporary index", name);
        }
@@@ -3627,7 -3241,7 +3627,7 @@@ static void stat_patch_list(struct patc
                show_stats(patch);
        }
  
 -      printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels);
 +      print_stat_summary(stdout, files, adds, dels);
  }
  
  static void numstat_patch_list(struct patch *patch)
@@@ -3747,7 -3361,7 +3747,7 @@@ static void remove_file(struct patch *p
  {
        if (update_index) {
                if (remove_file_from_cache(patch->old_name) < 0)
 -                      die("unable to remove %s from index", patch->old_name);
 +                      die(_("unable to remove %s from index"), patch->old_name);
        }
        if (!cached) {
                if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
@@@ -3774,19 -3388,19 +3774,19 @@@ static void add_index_file(const char *
                const char *s = buf;
  
                if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
 -                      die("corrupt patch for subproject %s", path);
 +                      die(_("corrupt patch for subproject %s"), path);
        } else {
                if (!cached) {
                        if (lstat(path, &st) < 0)
 -                              die_errno("unable to stat newly created file '%s'",
 +                              die_errno(_("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);
 +                      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);
 +              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)
        strbuf_release(&nbuf);
  
        if (close(fd) < 0)
 -              die_errno("closing file '%s'", path);
 +              die_errno(_("closing file '%s'"), path);
        return 0;
  }
  
@@@ -3868,33 -3482,7 +3868,33 @@@ static void create_one_file(char *path
                        ++nr;
                }
        }
 -      die_errno("unable to write file '%s' mode %o", path, mode);
 +      die_errno(_("unable to write file '%s' mode %o"), path, mode);
 +}
 +
 +static void add_conflicted_stages_file(struct patch *patch)
 +{
 +      int stage, namelen;
 +      unsigned ce_size, mode;
 +      struct cache_entry *ce;
 +
 +      if (!update_index)
 +              return;
 +      namelen = strlen(patch->new_name);
 +      ce_size = cache_entry_size(namelen);
 +      mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);
 +
 +      remove_file_from_cache(patch->new_name);
 +      for (stage = 1; stage < 4; stage++) {
 +              if (is_null_sha1(patch->threeway_stage[stage - 1]))
 +                      continue;
 +              ce = xcalloc(1, ce_size);
 +              memcpy(ce->name, patch->new_name, namelen);
 +              ce->ce_mode = create_ce_mode(mode);
 +              ce->ce_flags = create_ce_flags(namelen, stage);
 +              hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
 +              if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
 +                      die(_("unable to add cache entry for %s"), patch->new_name);
 +      }
  }
  
  static void create_file(struct patch *patch)
        if (!mode)
                mode = S_IFREG | 0644;
        create_one_file(path, mode, buf, size);
 -      add_index_file(path, mode, buf, size);
 +
 +      if (patch->conflicted_threeway)
 +              add_conflicted_stages_file(patch);
 +      else
 +              add_index_file(path, mode, buf, size);
  }
  
  /* phase zero is to remove, phase one is to create */
@@@ -3943,7 -3527,6 +3943,7 @@@ static int write_out_one_reject(struct 
        char namebuf[PATH_MAX];
        struct fragment *frag;
        int cnt = 0;
 +      struct strbuf sb = STRBUF_INIT;
  
        for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
                if (!frag->rejected)
        if (!cnt) {
                if (apply_verbosely)
                        say_patch_name(stderr,
 -                                     "Applied patch ", patch, " cleanly.\n");
 +                                     _("Applied patch %s cleanly."), patch);
                return 0;
        }
  
         * contents are marked "rejected" at the patch level.
         */
        if (!patch->new_name)
 -              die("internal error");
 +              die(_("internal error"));
  
        /* Say this even without --verbose */
 -      say_patch_name(stderr, "Applying patch ", patch, " with");
 -      fprintf(stderr, " %d rejects...\n", cnt);
 +      strbuf_addf(&sb, Q_("Applying patch %%s with %d reject...",
 +                          "Applying patch %%s with %d rejects...",
 +                          cnt),
 +                  cnt);
 +      say_patch_name(stderr, sb.buf, patch);
 +      strbuf_release(&sb);
  
        cnt = strlen(patch->new_name);
        if (ARRAY_SIZE(namebuf) <= cnt + 5) {
                cnt = ARRAY_SIZE(namebuf) - 5;
 -              warning("truncating .rej filename to %.*s.rej",
 +              warning(_("truncating .rej filename to %.*s.rej"),
                        cnt - 1, patch->new_name);
        }
        memcpy(namebuf, patch->new_name, cnt);
  
        rej = fopen(namebuf, "w");
        if (!rej)
 -              return error("cannot open %s: %s", namebuf, strerror(errno));
 +              return error(_("cannot open %s: %s"), namebuf, strerror(errno));
  
        /* Normal git tools never deal with .rej, so do not pretend
         * this is a git patch by saying --git nor give extended
             frag;
             cnt++, frag = frag->next) {
                if (!frag->rejected) {
 -                      fprintf(stderr, "Hunk #%d applied cleanly.\n", cnt);
 +                      fprintf_ln(stderr, _("Hunk #%d applied cleanly."), cnt);
                        continue;
                }
 -              fprintf(stderr, "Rejected hunk #%d.\n", cnt);
 +              fprintf_ln(stderr, _("Rejected hunk #%d."), cnt);
                fprintf(rej, "%.*s", frag->size, frag->patch);
                if (frag->patch[frag->size-1] != '\n')
                        fputc('\n', rej);
@@@ -4013,7 -3592,6 +4013,7 @@@ static int write_out_results(struct pat
        int phase;
        int errs = 0;
        struct patch *l;
 +      struct string_list cpath = STRING_LIST_INIT_DUP;
  
        for (phase = 0; phase < 2; phase++) {
                l = list;
                                errs = 1;
                        else {
                                write_out_one_result(l, phase);
 -                              if (phase == 1 && write_out_one_reject(l))
 -                                      errs = 1;
 +                              if (phase == 1) {
 +                                      if (write_out_one_reject(l))
 +                                              errs = 1;
 +                                      if (l->conflicted_threeway) {
 +                                              string_list_append(&cpath, l->new_name);
 +                                              errs = 1;
 +                                      }
 +                              }
                        }
                        l = l->next;
                }
        }
 +
 +      if (cpath.nr) {
 +              struct string_list_item *item;
 +
 +              sort_string_list(&cpath);
 +              for_each_string_list_item(item, &cpath)
 +                      fprintf(stderr, "U %s\n", item->string);
 +              string_list_clear(&cpath, 0);
 +
 +              rerere(0);
 +      }
 +
        return errs;
  }
  
@@@ -4104,8 -3664,15 +4104,8 @@@ static void prefix_patches(struct patc
        if (!prefix || p->is_toplevel_relative)
                return;
        for ( ; p; p = p->next) {
 -              if (p->new_name == p->old_name) {
 -                      char *prefixed = p->new_name;
 -                      prefix_one(&prefixed);
 -                      p->new_name = p->old_name = prefixed;
 -              }
 -              else {
 -                      prefix_one(&p->new_name);
 -                      prefix_one(&p->old_name);
 -              }
 +              prefix_one(&p->new_name);
 +              prefix_one(&p->old_name);
        }
  }
  
  static int apply_patch(int fd, const char *filename, int options)
  {
        size_t offset;
 -      struct strbuf buf = STRBUF_INIT;
 +      struct strbuf buf = STRBUF_INIT; /* owns the patch text */
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
  
 -      /* FIXME - memory leak when using multiple patch files as inputs */
 -      memset(&fn_table, 0, sizeof(struct string_list));
        patch_input_file = filename;
        read_patch_file(&buf, fd);
        offset = 0;
                        listp = &patch->next;
                }
                else {
 -                      /* perhaps free it a bit better? */
 -                      free(patch);
 +                      free_patch(patch);
                        skipped_patch++;
                }
                offset += nr;
        }
  
        if (!list && !skipped_patch)
 -              die("unrecognized input");
 +              die(_("unrecognized input"));
  
        if (whitespace_error && (ws_error_action == die_on_ws_error))
                apply = 0;
  
        if (check_index) {
                if (read_cache() < 0)
 -                      die("unable to read index file");
 +                      die(_("unable to read index file"));
        }
  
        if ((check || apply) &&
            !apply_with_reject)
                exit(1);
  
 -      if (apply && write_out_results(list))
 -              exit(1);
 +      if (apply && write_out_results(list)) {
 +              if (apply_with_reject)
 +                      exit(1);
 +              /* with --3way, we still need to write the index out */
 +              return 1;
 +      }
  
        if (fake_ancestor)
                build_fake_ancestor(list, fake_ancestor);
        if (summary)
                summary_patch_list(list);
  
 +      free_patch_list(list);
        strbuf_release(&buf);
 +      string_list_clear(&fn_table, 0);
        return 0;
  }
  
@@@ -4279,68 -3843,66 +4279,68 @@@ int cmd_apply(int argc, const char **ar
        const char *whitespace_option = NULL;
  
        struct option builtin_apply_options[] = {
 -              { OPTION_CALLBACK, 0, "exclude", NULL, "path",
 -                      "don't apply changes matching the given path",
 +              { OPTION_CALLBACK, 0, "exclude", NULL, N_("path"),
 +                      N_("don't apply changes matching the given path"),
                        0, option_parse_exclude },
 -              { OPTION_CALLBACK, 0, "include", NULL, "path",
 -                      "apply changes matching the given path",
 +              { OPTION_CALLBACK, 0, "include", NULL, N_("path"),
 +                      N_("apply changes matching the given path"),
                        0, option_parse_include },
 -              { OPTION_CALLBACK, 'p', NULL, NULL, "num",
 -                      "remove <num> leading slashes from traditional diff paths",
 +              { OPTION_CALLBACK, 'p', NULL, NULL, N_("num"),
 +                      N_("remove <num> leading slashes from traditional diff paths"),
                        0, option_parse_p },
                OPT_BOOLEAN(0, "no-add", &no_add,
 -                      "ignore additions made by the patch"),
 +                      N_("ignore additions made by the patch")),
                OPT_BOOLEAN(0, "stat", &diffstat,
 -                      "instead of applying the patch, output diffstat for the input"),
 +                      N_("instead of applying the patch, output diffstat for the input")),
                OPT_NOOP_NOARG(0, "allow-binary-replacement"),
                OPT_NOOP_NOARG(0, "binary"),
                OPT_BOOLEAN(0, "numstat", &numstat,
 -                      "shows number of added and deleted lines in decimal notation"),
 +                      N_("shows number of added and deleted lines in decimal notation")),
                OPT_BOOLEAN(0, "summary", &summary,
 -                      "instead of applying the patch, output a summary for the input"),
 +                      N_("instead of applying the patch, output a summary for the input")),
                OPT_BOOLEAN(0, "check", &check,
 -                      "instead of applying the patch, see if the patch is applicable"),
 +                      N_("instead of applying the patch, see if the patch is applicable")),
                OPT_BOOLEAN(0, "index", &check_index,
 -                      "make sure the patch is applicable to the current index"),
 +                      N_("make sure the patch is applicable to the current index")),
                OPT_BOOLEAN(0, "cached", &cached,
 -                      "apply a patch without touching the working tree"),
 +                      N_("apply a patch without touching the working tree")),
                OPT_BOOLEAN(0, "apply", &force_apply,
 -                      "also apply the patch (use with --stat/--summary/--check)"),
 +                      N_("also apply the patch (use with --stat/--summary/--check)")),
 +              OPT_BOOL('3', "3way", &threeway,
 +                       N_( "attempt three-way merge if a patch does not apply")),
                OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
 -                      "build a temporary index based on embedded index information"),
 +                      N_("build a temporary index based on embedded index information")),
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
 -                      "paths are separated with NUL character",
 +                      N_("paths are separated with NUL character"),
                        PARSE_OPT_NOARG, option_parse_z },
                OPT_INTEGER('C', NULL, &p_context,
 -                              "ensure at least <n> lines of context match"),
 -              { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
 -                      "detect new or modified lines that have whitespace errors",
 +                              N_("ensure at least <n> lines of context match")),
 +              { OPTION_CALLBACK, 0, "whitespace", &whitespace_option, N_("action"),
 +                      N_("detect new or modified lines that have whitespace errors"),
                        0, option_parse_whitespace },
                { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
 -                      "ignore changes in whitespace when finding context",
 +                      N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
                { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
 -                      "ignore changes in whitespace when finding context",
 +                      N_("ignore changes in whitespace when finding context"),
                        PARSE_OPT_NOARG, option_parse_space_change },
                OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
 -                      "apply the patch in reverse"),
 +                      N_("apply the patch in reverse")),
                OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
 -                      "don't expect at least one line of context"),
 +                      N_("don't expect at least one line of context")),
                OPT_BOOLEAN(0, "reject", &apply_with_reject,
 -                      "leave the rejected hunks in corresponding *.rej files"),
 +                      N_("leave the rejected hunks in corresponding *.rej files")),
                OPT_BOOLEAN(0, "allow-overlap", &allow_overlap,
 -                      "allow overlapping hunks"),
 -              OPT__VERBOSE(&apply_verbosely, "be verbose"),
 +                      N_("allow overlapping hunks")),
 +              OPT__VERBOSE(&apply_verbosely, N_("be verbose")),
                OPT_BIT(0, "inaccurate-eof", &options,
 -                      "tolerate incorrectly detected missing new-line at the end of file",
 +                      N_("tolerate incorrectly detected missing new-line at the end of file"),
                        INACCURATE_EOF),
                OPT_BIT(0, "recount", &options,
 -                      "do not trust the line counts in the hunk headers",
 +                      N_("do not trust the line counts in the hunk headers"),
                        RECOUNT),
 -              { OPTION_CALLBACK, 0, "directory", NULL, "root",
 -                      "prepend <root> to all filenames",
 +              { OPTION_CALLBACK, 0, "directory", NULL, N_("root"),
 +                      N_("prepend <root> to all filenames"),
                        0, option_parse_directory },
                OPT_END()
        };
        argc = parse_options(argc, argv, prefix, builtin_apply_options,
                        apply_usage, 0);
  
 +      if (apply_with_reject && threeway)
 +              die("--reject and --3way cannot be used together.");
 +      if (cached && threeway)
 +              die("--cached and --3way cannot be used together.");
 +      if (threeway) {
 +              if (is_not_gitdir)
 +                      die(_("--3way outside a repository"));
 +              check_index = 1;
 +      }
        if (apply_with_reject)
                apply = apply_verbosely = 1;
        if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
                apply = 0;
        if (check_index && is_not_gitdir)
 -              die("--index outside a repository");
 +              die(_("--index outside a repository"));
        if (cached) {
                if (is_not_gitdir)
 -                      die("--cached outside a repository");
 +                      die(_("--cached outside a repository"));
                check_index = 1;
        }
        for (i = 0; i < argc; i++) {
  
                fd = open(arg, O_RDONLY);
                if (fd < 0)
 -                      die_errno("can't open patch '%s'", arg);
 +                      die_errno(_("can't open patch '%s'"), arg);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
                errs |= apply_patch(fd, arg, options);
                    squelch_whitespace_errors < whitespace_error) {
                        int squelched =
                                whitespace_error - squelch_whitespace_errors;
 -                      warning("squelched %d "
 -                              "whitespace error%s",
 -                              squelched,
 -                              squelched == 1 ? "" : "s");
 +                      warning(Q_("squelched %d whitespace error",
 +                                 "squelched %d whitespace errors",
 +                                 squelched),
 +                              squelched);
                }
                if (ws_error_action == die_on_ws_error)
 -                      die("%d line%s add%s whitespace errors.",
 -                          whitespace_error,
 -                          whitespace_error == 1 ? "" : "s",
 -                          whitespace_error == 1 ? "s" : "");
 +                      die(Q_("%d line adds whitespace errors.",
 +                             "%d lines add whitespace errors.",
 +                             whitespace_error),
 +                          whitespace_error);
                if (applied_after_fixing_ws && apply)
                        warning("%d line%s applied after"
                                " fixing whitespace errors.",
                                applied_after_fixing_ws,
                                applied_after_fixing_ws == 1 ? "" : "s");
                else if (whitespace_error)
 -                      warning("%d line%s add%s whitespace errors.",
 -                              whitespace_error,
 -                              whitespace_error == 1 ? "" : "s",
 -                              whitespace_error == 1 ? "s" : "");
 +                      warning(Q_("%d line adds whitespace errors.",
 +                                 "%d lines add whitespace errors.",
 +                                 whitespace_error),
 +                              whitespace_error);
        }
  
        if (update_index) {
                if (write_cache(newfd, active_cache, active_nr) ||
                    commit_locked_index(&lock_file))
 -                      die("Unable to write new index file");
 +                      die(_("Unable to write new index file"));
        }
  
        return !!errs;
diff --combined builtin/cat-file.c
index 36a9104433e23422aab39b1912e998a7f54cd3f4,c27268ff30ab0e818b56f3e0a3ab56028083c6c1..af74e775a182bdf764436362cb37471e74930459
@@@ -11,7 -11,6 +11,7 @@@
  #include "parse-options.h"
  #include "diff.h"
  #include "userdiff.h"
 +#include "streaming.h"
  
  #define BATCH 1
  #define BATCH_CHECK 2
@@@ -91,7 -90,7 +91,7 @@@ static int cat_one_file(int opt, const 
        unsigned long size;
        struct object_context obj_context;
  
-       if (get_sha1_with_context(obj_name, sha1, &obj_context))
+       if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
                die("Not a valid object name %s", obj_name);
  
        buf = NULL;
                        return cmd_ls_tree(2, ls_args, NULL);
                }
  
 +              if (type == OBJ_BLOB)
 +                      return stream_blob_to_fd(1, sha1, NULL, 0);
                buf = read_sha1_file(sha1, &type, &size);
                if (!buf)
                        die("Cannot read object %s", obj_name);
                break;
  
        case 0:
 +              if (type_from_string(exp_type) == OBJ_BLOB) {
 +                      unsigned char blob_sha1[20];
 +                      if (sha1_object_info(sha1, NULL) == OBJ_TAG) {
 +                              enum object_type type;
 +                              unsigned long size;
 +                              char *buffer = read_sha1_file(sha1, &type, &size);
 +                              if (memcmp(buffer, "object ", 7) ||
 +                                  get_sha1_hex(buffer + 7, blob_sha1))
 +                                      die("%s not a valid tag", sha1_to_hex(sha1));
 +                              free(buffer);
 +                      } else
 +                              hashcpy(blob_sha1, sha1);
 +
 +                      if (sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
 +                              return stream_blob_to_fd(1, blob_sha1, NULL, 0);
 +                      /*
 +                       * we attempted to dereference a tag to a blob
 +                       * and failed; there may be new dereference
 +                       * mechanisms this code is not aware of.
 +                       * fall-back to the usual case.
 +                       */
 +              }
                buf = read_object_with_reference(sha1, exp_type, &size, NULL);
                break;
  
@@@ -251,8 -226,14 +251,8 @@@ static const char * const cat_file_usag
  
  static int git_cat_file_config(const char *var, const char *value, void *cb)
  {
 -      switch (userdiff_config(var, value)) {
 -      case 0:
 -              break;
 -      case -1:
 +      if (userdiff_config(var, value) < 0)
                return -1;
 -      default:
 -              return 0;
 -      }
  
        return git_default_config(var, value, cb);
  }
diff --combined builtin/log.c
index adcbcf1f2446573c99f00fb2480c8b582237cae1,9363f3929c1f3d5ac72ca498ada21f368f91072a..ecc2793690496531546765516d80032a1cbf8844
@@@ -20,8 -20,6 +20,8 @@@
  #include "string-list.h"
  #include "parse-options.h"
  #include "branch.h"
 +#include "streaming.h"
 +#include "version.h"
  
  /* Set a default date-time format for git log ("log.date" config variable) */
  static const char *default_date_mode = NULL;
@@@ -79,8 -77,6 +79,8 @@@ static void cmd_log_init_defaults(struc
                get_commit_format(fmt_pretty, rev);
        rev->verbose_header = 1;
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
 +      rev->diffopt.stat_width = -1; /* use full terminal width */
 +      rev->diffopt.stat_graph_width = -1; /* respect statGraphWidth config */
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
@@@ -367,6 -363,7 +367,7 @@@ int cmd_whatchanged(int argc, const cha
        rev.simplify_history = 0;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
+       opt.revarg_opt = REVARG_COMMITTISH;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
@@@ -385,13 -382,8 +386,13 @@@ static void show_tagger(char *buf, int 
        strbuf_release(&out);
  }
  
 -static int show_object(const unsigned char *sha1, int show_tag_object,
 -      struct rev_info *rev)
 +static int show_blob_object(const unsigned char *sha1, struct rev_info *rev)
 +{
 +      fflush(stdout);
 +      return stream_blob_to_fd(1, sha1, NULL, 0);
 +}
 +
 +static int show_tag_object(const unsigned char *sha1, struct rev_info *rev)
  {
        unsigned long size;
        enum object_type type;
        if (!buf)
                return error(_("Could not read object %s"), sha1_to_hex(sha1));
  
 -      if (show_tag_object)
 -              while (offset < size && buf[offset] != '\n') {
 -                      int new_offset = offset + 1;
 -                      while (new_offset < size && buf[new_offset++] != '\n')
 -                              ; /* do nothing */
 -                      if (!prefixcmp(buf + offset, "tagger "))
 -                              show_tagger(buf + offset + 7,
 -                                          new_offset - offset - 7, rev);
 -                      offset = new_offset;
 -              }
 +      assert(type == OBJ_TAG);
 +      while (offset < size && buf[offset] != '\n') {
 +              int new_offset = offset + 1;
 +              while (new_offset < size && buf[new_offset++] != '\n')
 +                      ; /* do nothing */
 +              if (!prefixcmp(buf + offset, "tagger "))
 +                      show_tagger(buf + offset + 7,
 +                                  new_offset - offset - 7, rev);
 +              offset = new_offset;
 +      }
  
        if (offset < size)
                fwrite(buf + offset, size - offset, 1, stdout);
@@@ -456,16 -448,11 +457,16 @@@ int cmd_show(int argc, const char **arg
        rev.diff = 1;
        rev.always_show_header = 1;
        rev.no_walk = 1;
 +      rev.diffopt.stat_width = -1;    /* Scale to real terminal size */
 +
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
        opt.tweak = show_rev_tweak_rev;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
  
 +      if (!rev.no_walk)
 +              return cmd_log_walk(&rev);
 +
        count = rev.pending.nr;
        objects = rev.pending.objects;
        for (i = 0; i < count && !ret; i++) {
                const char *name = objects[i].name;
                switch (o->type) {
                case OBJ_BLOB:
 -                      ret = show_object(o->sha1, 0, NULL);
 +                      ret = show_blob_object(o->sha1, NULL);
                        break;
                case OBJ_TAG: {
                        struct tag *t = (struct tag *)o;
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
 -                      ret = show_object(o->sha1, 1, &rev);
 +                      ret = show_tag_object(o->sha1, &rev);
                        rev.shown_one = 1;
                        if (ret)
                                break;
@@@ -557,6 -544,7 +558,7 @@@ int cmd_log(int argc, const char **argv
        rev.always_show_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
+       opt.revarg_opt = REVARG_COMMITTISH;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
        return cmd_log_walk(&rev);
  }
@@@ -667,8 -655,7 +669,8 @@@ static FILE *realstdout = NULL
  static const char *output_directory = NULL;
  static int outdir_offset;
  
 -static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet)
 +static int reopen_stdout(struct commit *commit, const char *subject,
 +                       struct rev_info *rev, int quiet)
  {
        struct strbuf filename = STRBUF_INIT;
        int suffix_len = strlen(fmt_patch_suffix) + 1;
                        strbuf_addch(&filename, '/');
        }
  
 -      get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
 +      get_patch_filename(commit, subject, rev->nr, fmt_patch_suffix, &filename);
  
        if (!quiet)
                fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
@@@ -742,10 -729,15 +744,10 @@@ static void get_patch_ids(struct rev_in
  
  static void gen_message_id(struct rev_info *info, char *base)
  {
 -      const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
 -      const char *email_start = strrchr(committer, '<');
 -      const char *email_end = strrchr(committer, '>');
        struct strbuf buf = STRBUF_INIT;
 -      if (!email_start || !email_end || email_start > email_end - 1)
 -              die(_("Could not extract email from committer identity."));
 -      strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
 +      strbuf_addf(&buf, "%s.%lu.git.%s", base,
                    (unsigned long) time(NULL),
 -                  (int)(email_end - email_start - 1), email_start + 1);
 +                  git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
        info->message_id = strbuf_detach(&buf, NULL);
  }
  
@@@ -784,6 -776,7 +786,6 @@@ static void make_cover_letter(struct re
        const char *encoding = "UTF-8";
        struct diff_options opts;
        int need_8bit_cte = 0;
 -      struct commit *commit = NULL;
        struct pretty_print_context pp = {0};
  
        if (rev->commit_format != CMIT_FMT_EMAIL)
  
        committer = git_committer_info(0);
  
 -      if (!numbered_files) {
 -              /*
 -               * We fake a commit for the cover letter so we get the filename
 -               * desired.
 -               */
 -              commit = xcalloc(1, sizeof(*commit));
 -              commit->buffer = xmalloc(400);
 -              snprintf(commit->buffer, 400,
 -                      "tree 0000000000000000000000000000000000000000\n"
 -                      "parent %s\n"
 -                      "author %s\n"
 -                      "committer %s\n\n"
 -                      "cover letter\n",
 -                      sha1_to_hex(head->object.sha1), committer, committer);
 -      }
 -
 -      if (!use_stdout && reopen_stdout(commit, rev, quiet))
 +      if (!use_stdout &&
 +          reopen_stdout(NULL, numbered_files ? NULL : "cover-letter", rev, quiet))
                return;
  
 -      if (commit) {
 -
 -              free(commit->buffer);
 -              free(commit);
 -      }
 -
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
  
@@@ -1132,6 -1146,7 +1134,7 @@@ int cmd_format_patch(int argc, const ch
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.def = "HEAD";
+       s_r_opt.revarg_opt = REVARG_COMMITTISH;
  
        if (default_attach) {
                rev.mime_boundary = default_attach;
        if (do_signoff) {
                const char *committer;
                const char *endpos;
 -              committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
 +              committer = git_committer_info(IDENT_STRICT);
                endpos = strchr(committer, '>');
                if (!endpos)
                        die(_("bogus committer info %s"), committer);
                        gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
                }
  
 -              if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
 -                                               &rev, quiet))
 +              if (!use_stdout &&
 +                  reopen_stdout(numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
diff --combined builtin/pack-objects.c
index f3348208d89a4fbeb3ae9f79376d7d21a8f9015c,48ccaddadf1a9cd9906867ccde62480c753355f1..782e7d0c38aa939a7db03266c3e6ff4993e4d2bd
  #include "list-objects.h"
  #include "progress.h"
  #include "refs.h"
 +#include "streaming.h"
  #include "thread-utils.h"
  
 -static const char pack_usage[] =
 -  "git pack-objects [ -q | --progress | --all-progress ]\n"
 -  "        [--all-progress-implied]\n"
 -  "        [--max-pack-size=<n>] [--local] [--incremental]\n"
 -  "        [--window=<n>] [--window-memory=<n>] [--depth=<n>]\n"
 -  "        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset]\n"
 -  "        [--threads=<n>] [--non-empty] [--revs [--unpacked | --all]]\n"
 -  "        [--reflog] [--stdout | base-name] [--include-tag]\n"
 -  "        [--keep-unreachable | --unpack-unreachable]\n"
 -  "        [< ref-list | < object-list]";
 +static const char *pack_usage[] = {
 +      "git pack-objects --stdout [options...] [< ref-list | < object-list]",
 +      "git pack-objects [options...] base-name [< ref-list | < object-list]",
 +      NULL
 +};
  
  struct object_entry {
        struct pack_idx_entry idx;
@@@ -64,7 -68,6 +64,7 @@@ static uint32_t nr_objects, nr_alloc, n
  static int non_empty;
  static int reuse_delta = 1, reuse_object = 1;
  static int keep_unreachable, unpack_unreachable, include_tag;
 +static unsigned long unpack_unreachable_expiration;
  static int local;
  static int incremental;
  static int ignore_packed_keep;
@@@ -151,46 -154,6 +151,46 @@@ static unsigned long do_compress(void *
        return stream.total_out;
  }
  
 +static unsigned long write_large_blob_data(struct git_istream *st, struct sha1file *f,
 +                                         const unsigned char *sha1)
 +{
 +      git_zstream stream;
 +      unsigned char ibuf[1024 * 16];
 +      unsigned char obuf[1024 * 16];
 +      unsigned long olen = 0;
 +
 +      memset(&stream, 0, sizeof(stream));
 +      git_deflate_init(&stream, pack_compression_level);
 +
 +      for (;;) {
 +              ssize_t readlen;
 +              int zret = Z_OK;
 +              readlen = read_istream(st, ibuf, sizeof(ibuf));
 +              if (readlen == -1)
 +                      die(_("unable to read %s"), sha1_to_hex(sha1));
 +
 +              stream.next_in = ibuf;
 +              stream.avail_in = readlen;
 +              while ((stream.avail_in || readlen == 0) &&
 +                     (zret == Z_OK || zret == Z_BUF_ERROR)) {
 +                      stream.next_out = obuf;
 +                      stream.avail_out = sizeof(obuf);
 +                      zret = git_deflate(&stream, readlen ? 0 : Z_FINISH);
 +                      sha1write(f, obuf, stream.next_out - obuf);
 +                      olen += stream.next_out - obuf;
 +              }
 +              if (stream.avail_in)
 +                      die(_("deflate error (%d)"), zret);
 +              if (readlen == 0) {
 +                      if (zret != Z_STREAM_END)
 +                              die(_("deflate error (%d)"), zret);
 +                      break;
 +              }
 +      }
 +      git_deflate_end(&stream);
 +      return olen;
 +}
 +
  /*
   * we are going to reuse the existing object data as is.  make
   * sure it is not corrupt.
@@@ -241,198 -204,22 +241,198 @@@ static void copy_pack_data(struct sha1f
  }
  
  /* Return 0 if we will bust the pack-size limit */
 -static unsigned long write_object(struct sha1file *f,
 -                                struct object_entry *entry,
 -                                off_t write_offset)
 +static unsigned long write_no_reuse_object(struct sha1file *f, struct object_entry *entry,
 +                                         unsigned long limit, int usable_delta)
  {
 -      unsigned long size, limit, datalen;
 -      void *buf;
 +      unsigned long size, datalen;
        unsigned char header[10], dheader[10];
        unsigned hdrlen;
        enum object_type type;
 +      void *buf;
 +      struct git_istream *st = NULL;
 +
 +      if (!usable_delta) {
 +              if (entry->type == OBJ_BLOB &&
 +                  entry->size > big_file_threshold &&
 +                  (st = open_istream(entry->idx.sha1, &type, &size, NULL)) != NULL)
 +                      buf = NULL;
 +              else {
 +                      buf = read_sha1_file(entry->idx.sha1, &type, &size);
 +                      if (!buf)
 +                              die(_("unable to read %s"), sha1_to_hex(entry->idx.sha1));
 +              }
 +              /*
 +               * make sure no cached delta data remains from a
 +               * previous attempt before a pack split occurred.
 +               */
 +              free(entry->delta_data);
 +              entry->delta_data = NULL;
 +              entry->z_delta_size = 0;
 +      } else if (entry->delta_data) {
 +              size = entry->delta_size;
 +              buf = entry->delta_data;
 +              entry->delta_data = NULL;
 +              type = (allow_ofs_delta && entry->delta->idx.offset) ?
 +                      OBJ_OFS_DELTA : OBJ_REF_DELTA;
 +      } else {
 +              buf = get_delta(entry);
 +              size = entry->delta_size;
 +              type = (allow_ofs_delta && entry->delta->idx.offset) ?
 +                      OBJ_OFS_DELTA : OBJ_REF_DELTA;
 +      }
 +
 +      if (st) /* large blob case, just assume we don't compress well */
 +              datalen = size;
 +      else if (entry->z_delta_size)
 +              datalen = entry->z_delta_size;
 +      else
 +              datalen = do_compress(&buf, size);
 +
 +      /*
 +       * The object header is a byte of 'type' followed by zero or
 +       * more bytes of length.
 +       */
 +      hdrlen = encode_in_pack_object_header(type, size, header);
 +
 +      if (type == OBJ_OFS_DELTA) {
 +              /*
 +               * Deltas with relative base contain an additional
 +               * encoding of the relative offset for the delta
 +               * base from this object's position in the pack.
 +               */
 +              off_t ofs = entry->idx.offset - entry->delta->idx.offset;
 +              unsigned pos = sizeof(dheader) - 1;
 +              dheader[pos] = ofs & 127;
 +              while (ofs >>= 7)
 +                      dheader[--pos] = 128 | (--ofs & 127);
 +              if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
 +                      if (st)
 +                              close_istream(st);
 +                      free(buf);
 +                      return 0;
 +              }
 +              sha1write(f, header, hdrlen);
 +              sha1write(f, dheader + pos, sizeof(dheader) - pos);
 +              hdrlen += sizeof(dheader) - pos;
 +      } else if (type == OBJ_REF_DELTA) {
 +              /*
 +               * Deltas with a base reference contain
 +               * an additional 20 bytes for the base sha1.
 +               */
 +              if (limit && hdrlen + 20 + datalen + 20 >= limit) {
 +                      if (st)
 +                              close_istream(st);
 +                      free(buf);
 +                      return 0;
 +              }
 +              sha1write(f, header, hdrlen);
 +              sha1write(f, entry->delta->idx.sha1, 20);
 +              hdrlen += 20;
 +      } else {
 +              if (limit && hdrlen + datalen + 20 >= limit) {
 +                      if (st)
 +                              close_istream(st);
 +                      free(buf);
 +                      return 0;
 +              }
 +              sha1write(f, header, hdrlen);
 +      }
 +      if (st) {
 +              datalen = write_large_blob_data(st, f, entry->idx.sha1);
 +              close_istream(st);
 +      } else {
 +              sha1write(f, buf, datalen);
 +              free(buf);
 +      }
 +
 +      return hdrlen + datalen;
 +}
 +
 +/* Return 0 if we will bust the pack-size limit */
 +static unsigned long write_reuse_object(struct sha1file *f, struct object_entry *entry,
 +                                      unsigned long limit, int usable_delta)
 +{
 +      struct packed_git *p = entry->in_pack;
 +      struct pack_window *w_curs = NULL;
 +      struct revindex_entry *revidx;
 +      off_t offset;
 +      enum object_type type = entry->type;
 +      unsigned long datalen;
 +      unsigned char header[10], dheader[10];
 +      unsigned hdrlen;
 +
 +      if (entry->delta)
 +              type = (allow_ofs_delta && entry->delta->idx.offset) ?
 +                      OBJ_OFS_DELTA : OBJ_REF_DELTA;
 +      hdrlen = encode_in_pack_object_header(type, entry->size, header);
 +
 +      offset = entry->in_pack_offset;
 +      revidx = find_pack_revindex(p, offset);
 +      datalen = revidx[1].offset - offset;
 +      if (!pack_to_stdout && p->index_version > 1 &&
 +          check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
 +              error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
 +              unuse_pack(&w_curs);
 +              return write_no_reuse_object(f, entry, limit, usable_delta);
 +      }
 +
 +      offset += entry->in_pack_header_size;
 +      datalen -= entry->in_pack_header_size;
 +
 +      if (!pack_to_stdout && p->index_version == 1 &&
 +          check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
 +              error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
 +              unuse_pack(&w_curs);
 +              return write_no_reuse_object(f, entry, limit, usable_delta);
 +      }
 +
 +      if (type == OBJ_OFS_DELTA) {
 +              off_t ofs = entry->idx.offset - entry->delta->idx.offset;
 +              unsigned pos = sizeof(dheader) - 1;
 +              dheader[pos] = ofs & 127;
 +              while (ofs >>= 7)
 +                      dheader[--pos] = 128 | (--ofs & 127);
 +              if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
 +                      unuse_pack(&w_curs);
 +                      return 0;
 +              }
 +              sha1write(f, header, hdrlen);
 +              sha1write(f, dheader + pos, sizeof(dheader) - pos);
 +              hdrlen += sizeof(dheader) - pos;
 +              reused_delta++;
 +      } else if (type == OBJ_REF_DELTA) {
 +              if (limit && hdrlen + 20 + datalen + 20 >= limit) {
 +                      unuse_pack(&w_curs);
 +                      return 0;
 +              }
 +              sha1write(f, header, hdrlen);
 +              sha1write(f, entry->delta->idx.sha1, 20);
 +              hdrlen += 20;
 +              reused_delta++;
 +      } else {
 +              if (limit && hdrlen + datalen + 20 >= limit) {
 +                      unuse_pack(&w_curs);
 +                      return 0;
 +              }
 +              sha1write(f, header, hdrlen);
 +      }
 +      copy_pack_data(f, p, &w_curs, offset, datalen);
 +      unuse_pack(&w_curs);
 +      reused++;
 +      return hdrlen + datalen;
 +}
 +
 +/* Return 0 if we will bust the pack-size limit */
 +static unsigned long write_object(struct sha1file *f,
 +                                struct object_entry *entry,
 +                                off_t write_offset)
 +{
 +      unsigned long limit, len;
        int usable_delta, to_reuse;
  
        if (!pack_to_stdout)
                crc32_begin(f);
  
 -      type = entry->type;
 -
        /* apply size limit if limited packsize and not first object */
        if (!pack_size_limit || !nr_written)
                limit = 0;
                to_reuse = 0;   /* explicit */
        else if (!entry->in_pack)
                to_reuse = 0;   /* can't reuse what we don't have */
 -      else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA)
 +      else if (entry->type == OBJ_REF_DELTA || entry->type == OBJ_OFS_DELTA)
                                /* check_object() decided it for us ... */
                to_reuse = usable_delta;
                                /* ... but pack split may override that */
 -      else if (type != entry->in_pack_type)
 +      else if (entry->type != entry->in_pack_type)
                to_reuse = 0;   /* pack has delta which is unusable */
        else if (entry->delta)
                to_reuse = 0;   /* we want to pack afresh */
                                 * and we do not need to deltify it.
                                 */
  
 -      if (!to_reuse) {
 -              no_reuse:
 -              if (!usable_delta) {
 -                      buf = read_sha1_file(entry->idx.sha1, &type, &size);
 -                      if (!buf)
 -                              die("unable to read %s", sha1_to_hex(entry->idx.sha1));
 -                      /*
 -                       * make sure no cached delta data remains from a
 -                       * previous attempt before a pack split occurred.
 -                       */
 -                      free(entry->delta_data);
 -                      entry->delta_data = NULL;
 -                      entry->z_delta_size = 0;
 -              } else if (entry->delta_data) {
 -                      size = entry->delta_size;
 -                      buf = entry->delta_data;
 -                      entry->delta_data = NULL;
 -                      type = (allow_ofs_delta && entry->delta->idx.offset) ?
 -                              OBJ_OFS_DELTA : OBJ_REF_DELTA;
 -              } else {
 -                      buf = get_delta(entry);
 -                      size = entry->delta_size;
 -                      type = (allow_ofs_delta && entry->delta->idx.offset) ?
 -                              OBJ_OFS_DELTA : OBJ_REF_DELTA;
 -              }
 -
 -              if (entry->z_delta_size)
 -                      datalen = entry->z_delta_size;
 -              else
 -                      datalen = do_compress(&buf, size);
 -
 -              /*
 -               * The object header is a byte of 'type' followed by zero or
 -               * more bytes of length.
 -               */
 -              hdrlen = encode_in_pack_object_header(type, size, header);
 -
 -              if (type == OBJ_OFS_DELTA) {
 -                      /*
 -                       * Deltas with relative base contain an additional
 -                       * encoding of the relative offset for the delta
 -                       * base from this object's position in the pack.
 -                       */
 -                      off_t ofs = entry->idx.offset - entry->delta->idx.offset;
 -                      unsigned pos = sizeof(dheader) - 1;
 -                      dheader[pos] = ofs & 127;
 -                      while (ofs >>= 7)
 -                              dheader[--pos] = 128 | (--ofs & 127);
 -                      if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
 -                              free(buf);
 -                              return 0;
 -                      }
 -                      sha1write(f, header, hdrlen);
 -                      sha1write(f, dheader + pos, sizeof(dheader) - pos);
 -                      hdrlen += sizeof(dheader) - pos;
 -              } else if (type == OBJ_REF_DELTA) {
 -                      /*
 -                       * Deltas with a base reference contain
 -                       * an additional 20 bytes for the base sha1.
 -                       */
 -                      if (limit && hdrlen + 20 + datalen + 20 >= limit) {
 -                              free(buf);
 -                              return 0;
 -                      }
 -                      sha1write(f, header, hdrlen);
 -                      sha1write(f, entry->delta->idx.sha1, 20);
 -                      hdrlen += 20;
 -              } else {
 -                      if (limit && hdrlen + datalen + 20 >= limit) {
 -                              free(buf);
 -                              return 0;
 -                      }
 -                      sha1write(f, header, hdrlen);
 -              }
 -              sha1write(f, buf, datalen);
 -              free(buf);
 -      }
 -      else {
 -              struct packed_git *p = entry->in_pack;
 -              struct pack_window *w_curs = NULL;
 -              struct revindex_entry *revidx;
 -              off_t offset;
 -
 -              if (entry->delta)
 -                      type = (allow_ofs_delta && entry->delta->idx.offset) ?
 -                              OBJ_OFS_DELTA : OBJ_REF_DELTA;
 -              hdrlen = encode_in_pack_object_header(type, entry->size, header);
 -
 -              offset = entry->in_pack_offset;
 -              revidx = find_pack_revindex(p, offset);
 -              datalen = revidx[1].offset - offset;
 -              if (!pack_to_stdout && p->index_version > 1 &&
 -                  check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
 -                      error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
 -                      unuse_pack(&w_curs);
 -                      goto no_reuse;
 -              }
 -
 -              offset += entry->in_pack_header_size;
 -              datalen -= entry->in_pack_header_size;
 -              if (!pack_to_stdout && p->index_version == 1 &&
 -                  check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
 -                      error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
 -                      unuse_pack(&w_curs);
 -                      goto no_reuse;
 -              }
 +      if (!to_reuse)
 +              len = write_no_reuse_object(f, entry, limit, usable_delta);
 +      else
 +              len = write_reuse_object(f, entry, limit, usable_delta);
 +      if (!len)
 +              return 0;
  
 -              if (type == OBJ_OFS_DELTA) {
 -                      off_t ofs = entry->idx.offset - entry->delta->idx.offset;
 -                      unsigned pos = sizeof(dheader) - 1;
 -                      dheader[pos] = ofs & 127;
 -                      while (ofs >>= 7)
 -                              dheader[--pos] = 128 | (--ofs & 127);
 -                      if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
 -                              unuse_pack(&w_curs);
 -                              return 0;
 -                      }
 -                      sha1write(f, header, hdrlen);
 -                      sha1write(f, dheader + pos, sizeof(dheader) - pos);
 -                      hdrlen += sizeof(dheader) - pos;
 -                      reused_delta++;
 -              } else if (type == OBJ_REF_DELTA) {
 -                      if (limit && hdrlen + 20 + datalen + 20 >= limit) {
 -                              unuse_pack(&w_curs);
 -                              return 0;
 -                      }
 -                      sha1write(f, header, hdrlen);
 -                      sha1write(f, entry->delta->idx.sha1, 20);
 -                      hdrlen += 20;
 -                      reused_delta++;
 -              } else {
 -                      if (limit && hdrlen + datalen + 20 >= limit) {
 -                              unuse_pack(&w_curs);
 -                              return 0;
 -                      }
 -                      sha1write(f, header, hdrlen);
 -              }
 -              copy_pack_data(f, p, &w_curs, offset, datalen);
 -              unuse_pack(&w_curs);
 -              reused++;
 -      }
        if (usable_delta)
                written_delta++;
        written++;
        if (!pack_to_stdout)
                entry->idx.crc32 = crc32_end(f);
 -      return hdrlen + datalen;
 +      return len;
  }
  
  enum write_one_status {
@@@ -1410,7 -1331,7 +1410,7 @@@ static void get_object_details(void
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *entry = sorted_by_offset[i];
                check_object(entry);
 -              if (big_file_threshold <= entry->size)
 +              if (big_file_threshold < entry->size)
                        entry->no_try_delta = 1;
        }
  
@@@ -2333,10 -2254,6 +2333,10 @@@ static void loosen_unused_packed_object
                if (!p->pack_local || p->pack_keep)
                        continue;
  
 +              if (unpack_unreachable_expiration &&
 +                  p->mtime < unpack_unreachable_expiration)
 +                      continue;
 +
                if (open_pack_index(p))
                        die("cannot open pack index");
  
@@@ -2373,7 -2290,7 +2373,7 @@@ static void get_object_list(int ac, con
                        }
                        die("not a rev '%s'", line);
                }
-               if (handle_revision_arg(line, &revs, flags, 1))
+               if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", line);
        }
  
                loosen_unused_packed_objects(&revs);
  }
  
 +static int option_parse_index_version(const struct option *opt,
 +                                    const char *arg, int unset)
 +{
 +      char *c;
 +      const char *val = arg;
 +      pack_idx_opts.version = strtoul(val, &c, 10);
 +      if (pack_idx_opts.version > 2)
 +              die(_("unsupported index version %s"), val);
 +      if (*c == ',' && c[1])
 +              pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
 +      if (*c || pack_idx_opts.off32_limit & 0x80000000)
 +              die(_("bad index version '%s'"), val);
 +      return 0;
 +}
 +
 +static int option_parse_unpack_unreachable(const struct option *opt,
 +                                         const char *arg, int unset)
 +{
 +      if (unset) {
 +              unpack_unreachable = 0;
 +              unpack_unreachable_expiration = 0;
 +      }
 +      else {
 +              unpack_unreachable = 1;
 +              if (arg)
 +                      unpack_unreachable_expiration = approxidate(arg);
 +      }
 +      return 0;
 +}
 +
 +static int option_parse_ulong(const struct option *opt,
 +                            const char *arg, int unset)
 +{
 +      if (unset)
 +              die(_("option %s does not accept negative form"),
 +                  opt->long_name);
 +
 +      if (!git_parse_ulong(arg, opt->value))
 +              die(_("unable to parse value '%s' for option %s"),
 +                  arg, opt->long_name);
 +      return 0;
 +}
 +
 +#define OPT_ULONG(s, l, v, h) \
 +      { OPTION_CALLBACK, (s), (l), (v), "n", (h),     \
 +        PARSE_OPT_NONEG, option_parse_ulong }
 +
  int cmd_pack_objects(int argc, const char **argv, const char *prefix)
  {
        int use_internal_rev_list = 0;
        int thin = 0;
        int all_progress_implied = 0;
 -      uint32_t i;
 -      const char **rp_av;
 -      int rp_ac_alloc = 64;
 -      int rp_ac;
 +      const char *rp_av[6];
 +      int rp_ac = 0;
 +      int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
 +      struct option pack_objects_options[] = {
 +              OPT_SET_INT('q', "quiet", &progress,
 +                          "do not show progress meter", 0),
 +              OPT_SET_INT(0, "progress", &progress,
 +                          "show progress meter", 1),
 +              OPT_SET_INT(0, "all-progress", &progress,
 +                          "show progress meter during object writing phase", 2),
 +              OPT_BOOL(0, "all-progress-implied",
 +                       &all_progress_implied,
 +                       "similar to --all-progress when progress meter is shown"),
 +              { OPTION_CALLBACK, 0, "index-version", NULL, "version[,offset]",
 +                "write the pack index file in the specified idx format version",
 +                0, option_parse_index_version },
 +              OPT_ULONG(0, "max-pack-size", &pack_size_limit,
 +                        "maximum size of each output pack file"),
 +              OPT_BOOL(0, "local", &local,
 +                       "ignore borrowed objects from alternate object store"),
 +              OPT_BOOL(0, "incremental", &incremental,
 +                       "ignore packed objects"),
 +              OPT_INTEGER(0, "window", &window,
 +                          "limit pack window by objects"),
 +              OPT_ULONG(0, "window-memory", &window_memory_limit,
 +                        "limit pack window by memory in addition to object limit"),
 +              OPT_INTEGER(0, "depth", &depth,
 +                          "maximum length of delta chain allowed in the resulting pack"),
 +              OPT_BOOL(0, "reuse-delta", &reuse_delta,
 +                       "reuse existing deltas"),
 +              OPT_BOOL(0, "reuse-object", &reuse_object,
 +                       "reuse existing objects"),
 +              OPT_BOOL(0, "delta-base-offset", &allow_ofs_delta,
 +                       "use OFS_DELTA objects"),
 +              OPT_INTEGER(0, "threads", &delta_search_threads,
 +                          "use threads when searching for best delta matches"),
 +              OPT_BOOL(0, "non-empty", &non_empty,
 +                       "do not create an empty pack output"),
 +              OPT_BOOL(0, "revs", &use_internal_rev_list,
 +                       "read revision arguments from standard input"),
 +              { OPTION_SET_INT, 0, "unpacked", &rev_list_unpacked, NULL,
 +                "limit the objects to those that are not yet packed",
 +                PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
 +              { OPTION_SET_INT, 0, "all", &rev_list_all, NULL,
 +                "include objects reachable from any reference",
 +                PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
 +              { OPTION_SET_INT, 0, "reflog", &rev_list_reflog, NULL,
 +                "include objects referred by reflog entries",
 +                PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, 1 },
 +              OPT_BOOL(0, "stdout", &pack_to_stdout,
 +                       "output pack to stdout"),
 +              OPT_BOOL(0, "include-tag", &include_tag,
 +                       "include tag objects that refer to objects to be packed"),
 +              OPT_BOOL(0, "keep-unreachable", &keep_unreachable,
 +                       "keep unreachable objects"),
 +              { OPTION_CALLBACK, 0, "unpack-unreachable", NULL, "time",
 +                "unpack unreachable objects newer than <time>",
 +                PARSE_OPT_OPTARG, option_parse_unpack_unreachable },
 +              OPT_BOOL(0, "thin", &thin,
 +                       "create thin packs"),
 +              OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
 +                       "ignore packs that have companion .keep file"),
 +              OPT_INTEGER(0, "compression", &pack_compression_level,
 +                          "pack compression level"),
 +              OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
 +                          "do not hide commits by grafts", 0),
 +              OPT_END(),
 +      };
  
        read_replace_refs = 0;
  
 -      rp_av = xcalloc(rp_ac_alloc, sizeof(*rp_av));
 -
 -      rp_av[0] = "pack-objects";
 -      rp_av[1] = "--objects"; /* --thin will make it --objects-edge */
 -      rp_ac = 2;
 -
        reset_pack_idx_option(&pack_idx_opts);
        git_config(git_pack_config, NULL);
        if (!pack_compression_seen && core_compression_seen)
                pack_compression_level = core_compression_level;
  
        progress = isatty(2);
 -      for (i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 +      argc = parse_options(argc, argv, prefix, pack_objects_options,
 +                           pack_usage, 0);
  
 -              if (*arg != '-')
 -                      break;
 -
 -              if (!strcmp("--non-empty", arg)) {
 -                      non_empty = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--local", arg)) {
 -                      local = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--incremental", arg)) {
 -                      incremental = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--honor-pack-keep", arg)) {
 -                      ignore_packed_keep = 1;
 -                      continue;
 -              }
 -              if (!prefixcmp(arg, "--compression=")) {
 -                      char *end;
 -                      int level = strtoul(arg+14, &end, 0);
 -                      if (!arg[14] || *end)
 -                              usage(pack_usage);
 -                      if (level == -1)
 -                              level = Z_DEFAULT_COMPRESSION;
 -                      else if (level < 0 || level > Z_BEST_COMPRESSION)
 -                              die("bad pack compression level %d", level);
 -                      pack_compression_level = level;
 -                      continue;
 -              }
 -              if (!prefixcmp(arg, "--max-pack-size=")) {
 -                      pack_size_limit_cfg = 0;
 -                      if (!git_parse_ulong(arg+16, &pack_size_limit))
 -                              usage(pack_usage);
 -                      continue;
 -              }
 -              if (!prefixcmp(arg, "--window=")) {
 -                      char *end;
 -                      window = strtoul(arg+9, &end, 0);
 -                      if (!arg[9] || *end)
 -                              usage(pack_usage);
 -                      continue;
 -              }
 -              if (!prefixcmp(arg, "--window-memory=")) {
 -                      if (!git_parse_ulong(arg+16, &window_memory_limit))
 -                              usage(pack_usage);
 -                      continue;
 -              }
 -              if (!prefixcmp(arg, "--threads=")) {
 -                      char *end;
 -                      delta_search_threads = strtoul(arg+10, &end, 0);
 -                      if (!arg[10] || *end || delta_search_threads < 0)
 -                              usage(pack_usage);
 -#ifdef NO_PTHREADS
 -                      if (delta_search_threads != 1)
 -                              warning("no threads support, "
 -                                      "ignoring %s", arg);
 -#endif
 -                      continue;
 -              }
 -              if (!prefixcmp(arg, "--depth=")) {
 -                      char *end;
 -                      depth = strtoul(arg+8, &end, 0);
 -                      if (!arg[8] || *end)
 -                              usage(pack_usage);
 -                      continue;
 -              }
 -              if (!strcmp("--progress", arg)) {
 -                      progress = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--all-progress", arg)) {
 -                      progress = 2;
 -                      continue;
 -              }
 -              if (!strcmp("--all-progress-implied", arg)) {
 -                      all_progress_implied = 1;
 -                      continue;
 -              }
 -              if (!strcmp("-q", arg)) {
 -                      progress = 0;
 -                      continue;
 -              }
 -              if (!strcmp("--no-reuse-delta", arg)) {
 -                      reuse_delta = 0;
 -                      continue;
 -              }
 -              if (!strcmp("--no-reuse-object", arg)) {
 -                      reuse_object = reuse_delta = 0;
 -                      continue;
 -              }
 -              if (!strcmp("--delta-base-offset", arg)) {
 -                      allow_ofs_delta = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--stdout", arg)) {
 -                      pack_to_stdout = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--revs", arg)) {
 -                      use_internal_rev_list = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--keep-unreachable", arg)) {
 -                      keep_unreachable = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--unpack-unreachable", arg)) {
 -                      unpack_unreachable = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--include-tag", arg)) {
 -                      include_tag = 1;
 -                      continue;
 -              }
 -              if (!strcmp("--unpacked", arg) ||
 -                  !strcmp("--reflog", arg) ||
 -                  !strcmp("--all", arg)) {
 -                      use_internal_rev_list = 1;
 -                      if (rp_ac >= rp_ac_alloc - 1) {
 -                              rp_ac_alloc = alloc_nr(rp_ac_alloc);
 -                              rp_av = xrealloc(rp_av,
 -                                               rp_ac_alloc * sizeof(*rp_av));
 -                      }
 -                      rp_av[rp_ac++] = arg;
 -                      continue;
 -              }
 -              if (!strcmp("--thin", arg)) {
 -                      use_internal_rev_list = 1;
 -                      thin = 1;
 -                      rp_av[1] = "--objects-edge";
 -                      continue;
 -              }
 -              if (!prefixcmp(arg, "--index-version=")) {
 -                      char *c;
 -                      pack_idx_opts.version = strtoul(arg + 16, &c, 10);
 -                      if (pack_idx_opts.version > 2)
 -                              die("bad %s", arg);
 -                      if (*c == ',')
 -                              pack_idx_opts.off32_limit = strtoul(c+1, &c, 0);
 -                      if (*c || pack_idx_opts.off32_limit & 0x80000000)
 -                              die("bad %s", arg);
 -                      continue;
 -              }
 -              if (!strcmp(arg, "--keep-true-parents")) {
 -                      grafts_replace_parents = 0;
 -                      continue;
 -              }
 -              usage(pack_usage);
 -      }
 -
 -      /* Traditionally "pack-objects [options] base extra" failed;
 -       * we would however want to take refs parameter that would
 -       * have been given to upstream rev-list ourselves, which means
 -       * we somehow want to say what the base name is.  So the
 -       * syntax would be:
 -       *
 -       * pack-objects [options] base <refs...>
 -       *
 -       * in other words, we would treat the first non-option as the
 -       * base_name and send everything else to the internal revision
 -       * walker.
 -       */
 +      if (argc) {
 +              base_name = argv[0];
 +              argc--;
 +      }
 +      if (pack_to_stdout != !base_name || argc)
 +              usage_with_options(pack_usage, pack_objects_options);
  
 -      if (!pack_to_stdout)
 -              base_name = argv[i++];
 +      rp_av[rp_ac++] = "pack-objects";
 +      if (thin) {
 +              use_internal_rev_list = 1;
 +              rp_av[rp_ac++] = "--objects-edge";
 +      } else
 +              rp_av[rp_ac++] = "--objects";
  
 -      if (pack_to_stdout != !base_name)
 -              usage(pack_usage);
 +      if (rev_list_all) {
 +              use_internal_rev_list = 1;
 +              rp_av[rp_ac++] = "--all";
 +      }
 +      if (rev_list_reflog) {
 +              use_internal_rev_list = 1;
 +              rp_av[rp_ac++] = "--reflog";
 +      }
 +      if (rev_list_unpacked) {
 +              use_internal_rev_list = 1;
 +              rp_av[rp_ac++] = "--unpacked";
 +      }
  
 +      if (!reuse_object)
 +              reuse_delta = 0;
 +      if (pack_compression_level == -1)
 +              pack_compression_level = Z_DEFAULT_COMPRESSION;
 +      else if (pack_compression_level < 0 || pack_compression_level > Z_BEST_COMPRESSION)
 +              die("bad pack compression level %d", pack_compression_level);
 +#ifdef NO_PTHREADS
 +      if (delta_search_threads != 1)
 +              warning("no threads support, ignoring --threads");
 +#endif
        if (!pack_to_stdout && !pack_size_limit)
                pack_size_limit = pack_size_limit_cfg;
        if (pack_to_stdout && pack_size_limit)
diff --combined builtin/reset.c
index 4cc34c908446fe2d3db5acf315e47f2768ba07bd,392fb6361ea7bd8913cfe5efdcda4c2ff04efc75..74442bd7663387ab9864927f9c0d511e04e6e379
@@@ -276,7 -276,7 +276,7 @@@ int cmd_reset(int argc, const char **ar
                 * Otherwise, argv[i] could be either <rev> or <paths> and
                 * has to be unambiguous.
                 */
-               else if (!get_sha1(argv[i], sha1)) {
+               else if (!get_sha1_committish(argv[i], sha1)) {
                        /*
                         * Ok, argv[i] looks like a rev; it should not
                         * be a filename.
                        rev = argv[i++];
                } else {
                        /* Otherwise we treat this as a filename */
 -                      verify_filename(prefix, argv[i]);
 +                      verify_filename(prefix, argv[i], 1);
                }
        }
  
-       if (get_sha1(rev, sha1))
+       if (get_sha1_committish(rev, sha1))
                die(_("Failed to resolve '%s' as a valid ref."), rev);
  
+       /*
+        * NOTE: As "git reset $treeish -- $path" should be usable on
+        * any tree-ish, this is not strictly correct. We are not
+        * moving the HEAD to any commit; we are merely resetting the
+        * entries in the index to that of a treeish.
+        */
        commit = lookup_commit_reference(sha1);
        if (!commit)
                die(_("Could not parse object '%s'."), rev);
diff --combined builtin/rev-parse.c
index 13495b88f5da1efc2094c0e69abfe93605ee8c03,d85b8a6a6d0c14ac992ef9e3c4aee7490c34e76f..32788a9f86427237d0f4838362c1e3bc722fa258
@@@ -195,6 -195,12 +195,12 @@@ static int anti_reference(const char *r
        return 0;
  }
  
+ static int show_abbrev(const unsigned char *sha1, void *cb_data)
+ {
+       show_rev(NORMAL, sha1, NULL);
+       return 0;
+ }
  static void show_datestring(const char *flag, const char *datestr)
  {
        static char buffer[100];
@@@ -238,7 -244,7 +244,7 @@@ static int try_difference(const char *a
                next = "HEAD";
        if (dotdot == arg)
                this = "HEAD";
-       if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
+       if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
                show_rev(NORMAL, end, next);
                show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
                if (symmetric) {
@@@ -278,7 -284,7 +284,7 @@@ static int try_parent_shorthands(const 
                return 0;
  
        *dotdot = 0;
-       if (get_sha1(arg, sha1))
+       if (get_sha1_committish(arg, sha1))
                return 0;
  
        if (!parents_only)
@@@ -486,7 -492,7 +492,7 @@@ int cmd_rev_parse(int argc, const char 
  
                if (as_is) {
                        if (show_file(arg) && as_is < 2)
 -                              verify_filename(prefix, arg);
 +                              verify_filename(prefix, arg, 0);
                        continue;
                }
                if (!strcmp(arg,"-n")) {
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
+                       if (!prefixcmp(arg, "--disambiguate=")) {
+                               for_each_abbrev(arg + 15, show_abbrev, NULL);
+                               continue;
+                       }
                        if (!strcmp(arg, "--bisect")) {
                                for_each_ref_in("refs/bisect/bad", show_reference, NULL);
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                        if (!strcmp(arg, "--show-prefix")) {
                                if (prefix)
                                        puts(prefix);
 +                              else
 +                                      putchar('\n');
                                continue;
                        }
                        if (!strcmp(arg, "--show-cdup")) {
                as_is = 1;
                if (!show_file(arg))
                        continue;
 -              verify_filename(prefix, arg);
 +              verify_filename(prefix, arg, 1);
        }
        if (verify) {
                if (revs_count == 1) {
diff --combined cache.h
index 6a1aff5e2aa0cbbdc6f1cb49425f45421b2ea18f,63388780a008236b3888af553007cc4329ec32b1..2997b918e8201516fe4df91f7cc3fbf8ee2253e4
+++ b/cache.h
@@@ -105,9 -105,6 +105,9 @@@ struct cache_header 
        unsigned int hdr_entries;
  };
  
 +#define INDEX_FORMAT_LB 2
 +#define INDEX_FORMAT_UB 4
 +
  /*
   * The "cache_time" is just the low 32 bits of the
   * time. It doesn't matter if it overflows - we only
@@@ -118,6 -115,48 +118,6 @@@ struct cache_time 
        unsigned int nsec;
  };
  
 -/*
 - * dev/ino/uid/gid/size are also just tracked to the low 32 bits
 - * Again - this is just a (very strong in practice) heuristic that
 - * the inode hasn't changed.
 - *
 - * We save the fields in big-endian order to allow using the
 - * index file over NFS transparently.
 - */
 -struct ondisk_cache_entry {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
 -/*
 - * This struct is used when CE_EXTENDED bit is 1
 - * The struct must match ondisk_cache_entry exactly from
 - * ctime till flags
 - */
 -struct ondisk_cache_entry_extended {
 -      struct cache_time ctime;
 -      struct cache_time mtime;
 -      unsigned int dev;
 -      unsigned int ino;
 -      unsigned int mode;
 -      unsigned int uid;
 -      unsigned int gid;
 -      unsigned int size;
 -      unsigned char sha1[20];
 -      unsigned short flags;
 -      unsigned short flags2;
 -      char name[FLEX_ARRAY]; /* more */
 -};
 -
  struct cache_entry {
        struct cache_time ce_ctime;
        struct cache_time ce_mtime;
@@@ -214,6 -253,9 +214,6 @@@ static inline size_t ce_namelen(const s
  }
  
  #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 -#define ondisk_ce_size(ce) (((ce)->ce_flags & CE_EXTENDED) ? \
 -                          ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
 -                          ondisk_cache_entry_size(ce_namelen(ce)))
  #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
  #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
  #define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
@@@ -264,11 -306,13 +264,11 @@@ static inline unsigned int canon_mode(u
        return S_IFGITLINK;
  }
  
 -#define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
 -#define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
 -#define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
  
  struct index_state {
        struct cache_entry **cache;
 +      unsigned int version;
        unsigned int cache_nr, cache_alloc, cache_changed;
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
@@@ -388,7 -432,6 +388,7 @@@ extern char *git_work_tree_cfg
  extern int is_inside_work_tree(void);
  extern int have_git_dir(void);
  extern const char *get_git_dir(void);
 +extern int is_git_directory(const char *path);
  extern char *get_object_directory(void);
  extern char *get_index_file(void);
  extern char *get_graft_file(void);
@@@ -409,11 -452,8 +409,11 @@@ extern const char *setup_git_directory(
  extern char *prefix_path(const char *prefix, int len, const char *path);
  extern const char *prefix_filename(const char *prefix, int len, const char *path);
  extern int check_filename(const char *prefix, const char *name);
 -extern void verify_filename(const char *prefix, const char *name);
 +extern void verify_filename(const char *prefix,
 +                          const char *name,
 +                          int diagnose_misspelt_rev);
  extern void verify_non_filename(const char *prefix, const char *name);
 +extern int path_inside_repo(const char *prefix, const char *path);
  
  #define INIT_DB_QUIET 0x0001
  
@@@ -563,7 -603,6 +563,7 @@@ extern int read_replace_refs
  extern int fsync_object_files;
  extern int core_preload_index;
  extern int core_apply_sparse_checkout;
 +extern int precomposed_unicode;
  
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
@@@ -584,10 -623,8 +584,10 @@@ enum rebase_setup_type 
  enum push_default_type {
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
 +      PUSH_DEFAULT_SIMPLE,
        PUSH_DEFAULT_UPSTREAM,
 -      PUSH_DEFAULT_CURRENT
 +      PUSH_DEFAULT_CURRENT,
 +      PUSH_DEFAULT_UNSPECIFIED
  };
  
  extern enum branch_track git_branch_track;
@@@ -623,8 -660,6 +623,8 @@@ extern char *git_snpath(char *buf, size
        __attribute__((format (printf, 3, 4)));
  extern char *git_pathdup(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
 +extern char *mkpathdup(const char *fmt, ...)
 +      __attribute__((format (printf, 1, 2)));
  
  /* Return a statically allocated filename matching the sha1 signature */
  extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
@@@ -672,19 -707,6 +672,19 @@@ static inline void hashclr(unsigned cha
  #define EMPTY_TREE_SHA1_BIN \
         ((const unsigned char *) EMPTY_TREE_SHA1_BIN_LITERAL)
  
 +#define EMPTY_BLOB_SHA1_HEX \
 +      "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
 +#define EMPTY_BLOB_SHA1_BIN_LITERAL \
 +      "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
 +      "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
 +#define EMPTY_BLOB_SHA1_BIN \
 +      ((const unsigned char *) EMPTY_BLOB_SHA1_BIN_LITERAL)
 +
 +static inline int is_empty_blob_sha1(const unsigned char *sha1)
 +{
 +      return !hashcmp(sha1, EMPTY_BLOB_SHA1_BIN);
 +}
 +
  int git_mkstemp(char *path, size_t n, const char *template);
  
  int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
@@@ -714,7 -736,6 +714,7 @@@ int set_shared_perm(const char *path, i
  int safe_create_leading_directories(char *path);
  int safe_create_leading_directories_const(const char *path);
  int mkdir_in_gitdir(const char *path);
 +extern void home_config_paths(char **global, char **xdg, char *file);
  extern char *expand_user_path(const char *path);
  const char *enter_repo(const char *path, int strict);
  static inline int is_absolute_path(const char *path)
@@@ -790,17 -811,25 +790,25 @@@ struct object_context 
        unsigned mode;
  };
  
+ #define GET_SHA1_QUIETLY        01
+ #define GET_SHA1_COMMIT         02
+ #define GET_SHA1_COMMITTISH     04
+ #define GET_SHA1_TREE          010
+ #define GET_SHA1_TREEISH       020
+ #define GET_SHA1_BLOB        040
+ #define GET_SHA1_ONLY_TO_DIE 04000
  extern int get_sha1(const char *str, unsigned char *sha1);
- extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);
- static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
- {
      return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
- }
- extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix);
- static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
- {
      return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
- }
+ extern int get_sha1_commit(const char *str, unsigned char *sha1);
+ extern int get_sha1_committish(const char *str, unsigned char *sha1);
+ extern int get_sha1_tree(const char *str, unsigned char *sha1);
extern int get_sha1_treeish(const char *str, unsigned char *sha1);
+ extern int get_sha1_blob(const char *str, unsigned char *sha1);
+ extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
+ extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
typedef int each_abbrev_fn(const unsigned char *sha1, void *);
+ extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
  /*
   * Try to read a SHA1 in hexadecimal format from the 40 characters
@@@ -884,8 -913,10 +892,8 @@@ enum date_mode 
  };
  
  const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 -const char *show_date_relative(unsigned long time, int tz,
 -                             const struct timeval *now,
 -                             char *timebuf,
 -                             size_t timebuf_size);
 +void show_date_relative(unsigned long time, int tz, const struct timeval *now,
 +                      struct strbuf *timebuf);
  int parse_date(const char *date, char *buf, int bufsize);
  int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
  void datestamp(char *buf, int bufsize);
@@@ -894,35 -925,15 +902,35 @@@ unsigned long approxidate_careful(cons
  unsigned long approxidate_relative(const char *date, const struct timeval *now);
  enum date_mode parse_date_format(const char *format);
  
 -#define IDENT_WARN_ON_NO_NAME  1
 -#define IDENT_ERROR_ON_NO_NAME 2
 -#define IDENT_NO_DATE        4
 +#define IDENT_STRICT         1
 +#define IDENT_NO_DATE        2
 +#define IDENT_NO_NAME        4
  extern const char *git_author_info(int);
  extern const char *git_committer_info(int);
  extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
  extern const char *fmt_name(const char *name, const char *email);
 +extern const char *ident_default_name(void);
 +extern const char *ident_default_email(void);
 +extern const char *ident_default_date(void);
  extern const char *git_editor(void);
  extern const char *git_pager(int stdout_is_tty);
 +extern int git_ident_config(const char *, const char *, void *);
 +
 +struct ident_split {
 +      const char *name_begin;
 +      const char *name_end;
 +      const char *mail_begin;
 +      const char *mail_end;
 +      const char *date_begin;
 +      const char *date_end;
 +      const char *tz_begin;
 +      const char *tz_end;
 +};
 +/*
 + * Signals an success with 0, but time part of the result may be NULL
 + * if the input lacks timestamp and zone
 + */
 +extern int split_ident_line(struct ident_split *, const char *, int);
  
  struct checkout {
        const char *base_dir;
@@@ -946,9 -957,7 +954,9 @@@ struct cache_def 
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
  extern int check_leading_path(const char *name, int len);
 +extern int threaded_check_leading_path(struct cache_def *cache, const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 +extern int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
  
@@@ -958,7 -967,6 +966,7 @@@ extern struct alternate_object_databas
        char base[FLEX_ARRAY]; /* more */
  } *alt_odb_list;
  extern void prepare_alt_odb(void);
 +extern void read_info_alternates(const char * relative_base, int depth);
  extern void add_to_alternates_file(const char *reference);
  typedef int alt_odb_fn(struct alternate_object_database *, void *);
  extern void foreach_alt_odb(alt_odb_fn, void*);
@@@ -1037,7 -1045,6 +1045,7 @@@ struct extra_have_objects 
  };
  extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
  extern int server_supports(const char *feature);
 +extern const char *parse_feature_request(const char *features, const char *feature);
  
  extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
  
@@@ -1114,8 -1121,6 +1122,8 @@@ extern int git_config_from_file(config_
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern int git_config(config_fn_t fn, void *);
 +extern int git_config_with_options(config_fn_t fn, void *,
 +                                 const char *filename, int respect_includes);
  extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
  extern int git_parse_ulong(const char *, unsigned long *);
  extern int git_config_int(const char *, const char *);
@@@ -1131,7 -1136,6 +1139,7 @@@ extern int git_config_parse_key(const c
  extern int git_config_set_multivar(const char *, const char *, const char *, int);
  extern int git_config_set_multivar_in_file(const char *, const char *, const char *, const char *, int);
  extern int git_config_rename_section(const char *, const char *);
 +extern int git_config_rename_section_in_file(const char *, const char *, const char *);
  extern const char *git_etc_gitconfig(void);
  extern int check_repository_format_version(const char *var, const char *value, void *cb);
  extern int git_env_bool(const char *, int);
@@@ -1142,14 -1146,11 +1150,14 @@@ extern const char *get_commit_output_en
  
  extern int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
  
 -extern const char *config_exclusive_filename;
 +struct config_include_data {
 +      int depth;
 +      config_fn_t fn;
 +      void *data;
 +};
 +#define CONFIG_INCLUDE_INIT { 0 }
 +extern int git_config_include(const char *name, const char *value, void *data);
  
 -#define MAX_GITNAME (1000)
 -extern char git_default_email[MAX_GITNAME];
 -extern char git_default_name[MAX_GITNAME];
  #define IDENT_NAME_GIVEN 01
  #define IDENT_MAIL_GIVEN 02
  #define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
@@@ -1182,8 -1183,6 +1190,8 @@@ extern void setup_pager(void)
  extern const char *pager_program;
  extern int pager_in_use(void);
  extern int pager_use_color;
 +extern int term_columns(void);
 +extern int decimal_width(int);
  
  extern const char *editor_program;
  extern const char *askpass_program;
@@@ -1270,6 -1269,4 +1278,6 @@@ extern struct startup_info *startup_inf
  /* builtin/merge.c */
  int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
  
 +int sane_execvp(const char *file, char *const argv[]);
 +
  #endif /* CACHE_H */
diff --combined commit.c
index 8248a994a50ab91874600567e544aa3735e4aa98,8b84efff98296abf97690a914750568103aebced..42af4c1f238b96eeb9e88b227b6f6c10ef317058
+++ b/commit.c
@@@ -7,7 -7,6 +7,7 @@@
  #include "revision.h"
  #include "notes.h"
  #include "gpg-interface.h"
 +#include "mergesort.h"
  
  int save_commit_buffer = 1;
  
@@@ -68,7 -67,7 +68,7 @@@ struct commit *lookup_commit_reference_
        unsigned char sha1[20];
        struct commit *commit;
  
-       if (get_sha1(name, sha1))
+       if (get_sha1_committish(name, sha1))
                return NULL;
        commit = lookup_commit_reference(sha1);
        if (!commit || parse_commit(commit))
@@@ -391,31 -390,15 +391,31 @@@ struct commit_list * commit_list_insert
        return commit_list_insert(item, pp);
  }
  
 +static int commit_list_compare_by_date(const void *a, const void *b)
 +{
 +      unsigned long a_date = ((const struct commit_list *)a)->item->date;
 +      unsigned long b_date = ((const struct commit_list *)b)->item->date;
 +      if (a_date < b_date)
 +              return 1;
 +      if (a_date > b_date)
 +              return -1;
 +      return 0;
 +}
 +
 +static void *commit_list_get_next(const void *a)
 +{
 +      return ((const struct commit_list *)a)->next;
 +}
 +
 +static void commit_list_set_next(void *a, void *next)
 +{
 +      ((struct commit_list *)a)->next = next;
 +}
  
  void commit_list_sort_by_date(struct commit_list **list)
  {
 -      struct commit_list *ret = NULL;
 -      while (*list) {
 -              commit_list_insert_by_date((*list)->item, &ret);
 -              *list = (*list)->next;
 -      }
 -      *list = ret;
 +      *list = llist_mergesort(*list, commit_list_get_next, commit_list_set_next,
 +                              commit_list_compare_by_date);
  }
  
  struct commit *pop_most_recent_commit(struct commit_list **list,
        return ret;
  }
  
 -void clear_commit_marks(struct commit *commit, unsigned int mark)
 +static void clear_commit_marks_1(struct commit_list **plist,
 +                               struct commit *commit, unsigned int mark)
  {
        while (commit) {
                struct commit_list *parents;
                        return;
  
                while ((parents = parents->next))
 -                      clear_commit_marks(parents->item, mark);
 +                      commit_list_insert(parents->item, plist);
  
                commit = commit->parents->item;
        }
  }
  
 +void clear_commit_marks(struct commit *commit, unsigned int mark)
 +{
 +      struct commit_list *list = NULL;
 +      commit_list_insert(commit, &list);
 +      while (list)
 +              clear_commit_marks_1(&list, pop_commit(&list), mark);
 +}
 +
  void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
  {
        struct object *object;
@@@ -1154,9 -1128,9 +1154,9 @@@ int commit_tree_extended(const struct s
  
        /* Person/date information */
        if (!author)
 -              author = git_author_info(IDENT_ERROR_ON_NO_NAME);
 +              author = git_author_info(IDENT_STRICT);
        strbuf_addf(&buffer, "author %s\n", author);
 -      strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
 +      strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));
        if (!encoding_is_utf8)
                strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
  
@@@ -1199,30 -1173,3 +1199,30 @@@ struct commit *get_merge_parent(const c
        }
        return commit;
  }
 +
 +/*
 + * Append a commit to the end of the commit_list.
 + *
 + * next starts by pointing to the variable that holds the head of an
 + * empty commit_list, and is updated to point to the "next" field of
 + * the last item on the list as new commits are appended.
 + *
 + * Usage example:
 + *
 + *     struct commit_list *list;
 + *     struct commit_list **next = &list;
 + *
 + *     next = commit_list_append(c1, next);
 + *     next = commit_list_append(c2, next);
 + *     assert(commit_list_count(list) == 2);
 + *     return list;
 + */
 +struct commit_list **commit_list_append(struct commit *commit,
 +                                      struct commit_list **next)
 +{
 +      struct commit_list *new = xmalloc(sizeof(struct commit_list));
 +      new->item = commit;
 +      *next = new;
 +      new->next = NULL;
 +      return &new->next;
 +}
diff --combined revision.c
index 5b81a92e3ac65ab0295f25198511fc83b4bbf1c9,ec6f0c8936a610be206ac88745ad91d94349a979..c64bf68fa1f84a226cec97ddaf1970cf0aaa308b
@@@ -139,32 -139,11 +139,32 @@@ void mark_tree_uninteresting(struct tre
  
  void mark_parents_uninteresting(struct commit *commit)
  {
 -      struct commit_list *parents = commit->parents;
 +      struct commit_list *parents = NULL, *l;
 +
 +      for (l = commit->parents; l; l = l->next)
 +              commit_list_insert(l->item, &parents);
  
        while (parents) {
                struct commit *commit = parents->item;
 -              if (!(commit->object.flags & UNINTERESTING)) {
 +              l = parents;
 +              parents = parents->next;
 +              free(l);
 +
 +              while (commit) {
 +                      /*
 +                       * A missing commit is ok iff its parent is marked
 +                       * uninteresting.
 +                       *
 +                       * We just mark such a thing parsed, so that when
 +                       * it is popped next time around, we won't be trying
 +                       * to parse it and get an error.
 +                       */
 +                      if (!has_sha1_file(commit->object.sha1))
 +                              commit->object.parsed = 1;
 +
 +                      if (commit->object.flags & UNINTERESTING)
 +                              break;
 +
                        commit->object.flags |= UNINTERESTING;
  
                        /*
                         * wasn't uninteresting), in which case we need
                         * to mark its parents recursively too..
                         */
 -                      if (commit->parents)
 -                              mark_parents_uninteresting(commit);
 -              }
 +                      if (!commit->parents)
 +                              break;
  
 -              /*
 -               * A missing commit is ok iff its parent is marked
 -               * uninteresting.
 -               *
 -               * We just mark such a thing parsed, so that when
 -               * it is popped next time around, we won't be trying
 -               * to parse it and get an error.
 -               */
 -              if (!has_sha1_file(commit->object.sha1))
 -                      commit->object.parsed = 1;
 -              parents = parents->next;
 +                      for (l = commit->parents->next; l; l = l->next)
 +                              commit_list_insert(l->item, &parents);
 +                      commit = commit->parents->item;
 +              }
        }
  }
  
@@@ -429,7 -416,7 +429,7 @@@ static int rev_same_tree_as_empty(struc
  static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
  {
        struct commit_list **pp, *parent;
 -      int tree_changed = 0, tree_same = 0;
 +      int tree_changed = 0, tree_same = 0, nth_parent = 0;
  
        /*
         * If we don't do pruning, everything is interesting
        while ((parent = *pp) != NULL) {
                struct commit *p = parent->item;
  
 +              /*
 +               * Do not compare with later parents when we care only about
 +               * the first parent chain, in order to avoid derailing the
 +               * traversal to follow a side branch that brought everything
 +               * in the path we are limited to by the pathspec.
 +               */
 +              if (revs->first_parent_only && nth_parent++)
 +                      break;
                if (parse_commit(p) < 0)
                        die("cannot simplify commit %s (because of %s)",
                            sha1_to_hex(commit->object.sha1),
@@@ -1000,7 -979,7 +1000,7 @@@ static int add_parents_only(struct rev_
                flags ^= UNINTERESTING;
                arg++;
        }
-       if (get_sha1(arg, sha1))
+       if (get_sha1_committish(arg, sha1))
                return 0;
        while (1) {
                it = get_reference(revs, arg, sha1, 0);
@@@ -1114,16 -1093,16 +1114,16 @@@ static void prepare_show_merge(struct r
        revs->limited = 1;
  }
  
- int handle_revision_arg(const char *arg_, struct rev_info *revs,
-                       int flags,
-                       int cant_be_filename)
+ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
  {
-       unsigned mode;
+       struct object_context oc;
        char *dotdot;
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
        const char *arg = arg_;
+       int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
+       unsigned get_sha1_flags = 0;
  
        dotdot = strstr(arg, "..");
        if (dotdot) {
                        next = "HEAD";
                if (dotdot == arg)
                        this = "HEAD";
-               if (!get_sha1(this, from_sha1) &&
-                   !get_sha1(next, sha1)) {
+               if (!get_sha1_committish(this, from_sha1) &&
+                   !get_sha1_committish(next, sha1)) {
                        struct commit *a, *b;
                        struct commit_list *exclude;
  
                local_flags = UNINTERESTING;
                arg++;
        }
-       if (get_sha1_with_mode(arg, sha1, &mode))
+       if (revarg_opt & REVARG_COMMITTISH)
+               get_sha1_flags = GET_SHA1_COMMITTISH;
+       if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
                return revs->ignore_missing ? 0 : -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
-       add_pending_object_with_mode(revs, object, arg, mode);
+       add_pending_object_with_mode(revs, object, arg, oc.mode);
        return 0;
  }
  
@@@ -1257,7 -1240,7 +1261,7 @@@ static void read_revisions_from_stdin(s
                        }
                        die("options not supported in --stdin mode");
                }
-               if (handle_revision_arg(sb.buf, revs, 0, 1))
+               if (handle_revision_arg(sb.buf, revs, 0, REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", sb.buf);
        }
        if (seen_dashdash)
@@@ -1358,13 -1341,11 +1362,13 @@@ static int handle_revision_opt(struct r
                revs->topo_order = 1;
        } else if (!strcmp(arg, "--simplify-merges")) {
                revs->simplify_merges = 1;
 +              revs->topo_order = 1;
                revs->rewrite_parents = 1;
                revs->simplify_history = 0;
                revs->limited = 1;
        } else if (!strcmp(arg, "--simplify-by-decoration")) {
                revs->simplify_merges = 1;
 +              revs->topo_order = 1;
                revs->rewrite_parents = 1;
                revs->simplify_history = 0;
                revs->simplify_by_decoration = 1;
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
 +              DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
@@@ -1708,7 -1688,7 +1712,7 @@@ static int handle_revision_pseudo_opt(c
   */
  int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
  {
-       int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
+       int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
        struct cmdline_pathspec prune_data;
        const char *submodule = NULL;
  
                submodule = opt->submodule;
  
        /* First, search for "--" */
 -      seen_dashdash = 0;
 -      for (i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 -              if (strcmp(arg, "--"))
 -                      continue;
 -              argv[i] = NULL;
 -              argc = i;
 -              if (argv[i + 1])
 -                      append_prune_data(&prune_data, argv + i + 1);
 +      if (opt && opt->assume_dashdash) {
                seen_dashdash = 1;
 -              break;
 +      } else {
 +              seen_dashdash = 0;
 +              for (i = 1; i < argc; i++) {
 +                      const char *arg = argv[i];
 +                      if (strcmp(arg, "--"))
 +                              continue;
 +                      argv[i] = NULL;
 +                      argc = i;
 +                      if (argv[i + 1])
 +                              append_prune_data(&prune_data, argv + i + 1);
 +                      seen_dashdash = 1;
 +                      break;
 +              }
        }
  
        /* Second, deal with arguments and options */
        flags = 0;
+       revarg_opt = opt ? opt->revarg_opt : 0;
+       if (seen_dashdash)
+               revarg_opt |= REVARG_CANNOT_BE_FILENAME;
        read_from_stdin = 0;
        for (left = i = 1; i < argc; i++) {
                const char *arg = argv[i];
                        continue;
                }
  
-               if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
+               if (handle_revision_arg(arg, revs, flags, revarg_opt)) {
                        int j;
                        if (seen_dashdash || *arg == '^')
                                die("bad revision '%s'", arg);
                         * but the latter we have checked in the main loop.
                         */
                        for (j = i; j < argc; j++)
 -                              verify_filename(revs->prefix, argv[j]);
 +                              verify_filename(revs->prefix, argv[j], j == i);
  
                        append_prune_data(&prune_data, argv + i);
                        break;
        if (revs->def && !revs->pending.nr && !got_rev_arg) {
                unsigned char sha1[20];
                struct object *object;
-               unsigned mode;
-               if (get_sha1_with_mode(revs->def, sha1, &mode))
+               struct object_context oc;
+               if (get_sha1_with_context(revs->def, 0, sha1, &oc))
                        die("bad default revision '%s'", revs->def);
                object = get_reference(revs, revs->def, sha1, 0);
-               add_pending_object_with_mode(revs, object, revs->def, mode);
+               add_pending_object_with_mode(revs, object, revs->def, oc.mode);
        }
  
        /* Did the user ask for any diff output? Run the diff! */
@@@ -1949,9 -1929,8 +1957,9 @@@ static struct commit_list **simplify_on
        }
  
        /*
 -       * Do we know what commit all of our parents should be rewritten to?
 -       * Otherwise we are not ready to rewrite this one yet.
 +       * Do we know what commit all of our parents that matter
 +       * should be rewritten to?  Otherwise we are not ready to
 +       * rewrite this one yet.
         */
        for (cnt = 0, p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
                        tail = &commit_list_insert(p->item, tail)->next;
                        cnt++;
                }
 +              if (revs->first_parent_only)
 +                      break;
        }
        if (cnt) {
                tail = &commit_list_insert(commit, tail)->next;
        for (p = commit->parents; p; p = p->next) {
                pst = locate_simplify_state(revs, p->item);
                p->item = pst->simplified;
 +              if (revs->first_parent_only)
 +                      break;
        }
 -      cnt = remove_duplicate_parents(commit);
 +      if (!revs->first_parent_only)
 +              cnt = remove_duplicate_parents(commit);
 +      else
 +              cnt = 1;
  
        /*
         * It is possible that we are a merge and one side branch
  
  static void simplify_merges(struct rev_info *revs)
  {
 -      struct commit_list *list;
 +      struct commit_list *list, *next;
        struct commit_list *yet_to_do, **tail;
 +      struct commit *commit;
  
 -      if (!revs->topo_order)
 -              sort_in_topological_order(&revs->commits, revs->lifo);
        if (!revs->prune)
                return;
  
        /* feed the list reversed */
        yet_to_do = NULL;
 -      for (list = revs->commits; list; list = list->next)
 -              commit_list_insert(list->item, &yet_to_do);
 +      for (list = revs->commits; list; list = next) {
 +              commit = list->item;
 +              next = list->next;
 +              /*
 +               * Do not free(list) here yet; the original list
 +               * is used later in this function.
 +               */
 +              commit_list_insert(commit, &yet_to_do);
 +      }
        while (yet_to_do) {
                list = yet_to_do;
                yet_to_do = NULL;
                tail = &yet_to_do;
                while (list) {
 -                      struct commit *commit = list->item;
 -                      struct commit_list *next = list->next;
 +                      commit = list->item;
 +                      next = list->next;
                        free(list);
                        list = next;
                        tail = simplify_one(revs, commit, tail);
        revs->commits = NULL;
        tail = &revs->commits;
        while (list) {
 -              struct commit *commit = list->item;
 -              struct commit_list *next = list->next;
                struct merge_simplify_state *st;
 +
 +              commit = list->item;
 +              next = list->next;
                free(list);
                list = next;
                st = locate_simplify_state(revs, commit);
@@@ -2083,16 -2048,10 +2091,16 @@@ static void set_children(struct rev_inf
        }
  }
  
 +void reset_revision_walk(void)
 +{
 +      clear_object_flags(SEEN | ADDED | SHOWN);
 +}
 +
  int prepare_revision_walk(struct rev_info *revs)
  {
        int nr = revs->pending.nr;
        struct object_array_entry *e, *list;
 +      struct commit_list **next = &revs->commits;
  
        e = list = revs->pending.objects;
        revs->pending.nr = 0;
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
 -                              commit_list_insert_by_date(commit, &revs->commits);
 +                              next = commit_list_append(commit, next);
                        }
                }
                e++;
        }
 +      commit_list_sort_by_date(&revs->commits);
        if (!revs->leak_pending)
                free(list);
  
@@@ -2178,6 -2136,7 +2186,6 @@@ static int commit_match(struct commit *
        if (!opt->grep_filter.pattern_list && !opt->grep_filter.header_list)
                return 1;
        return grep_buffer(&opt->grep_filter,
 -                         NULL, /* we say nothing, not even filename */
                           commit->buffer, strlen(commit->buffer));
  }
  
diff --combined revision.h
index 863f4f64543bd9f9e830e7c9567def23e906c7d9,402f10d0af6df98365170559cd15fd9d12aa9c61..cb5ab3513bc9982095e292119b9119d333556256
@@@ -183,7 -183,7 +183,8 @@@ struct setup_revision_opt 
        const char *def;
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
        const char *submodule;
 +      int assume_dashdash;
+       unsigned revarg_opt;
  };
  
  extern void init_revisions(struct rev_info *revs, const char *prefix);
@@@ -191,9 -191,10 +192,11 @@@ extern int setup_revisions(int argc, co
  extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
                                 const struct option *options,
                                 const char * const usagestr[]);
- extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
+ #define REVARG_CANNOT_BE_FILENAME 01
+ #define REVARG_COMMITTISH 02
+ extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
  
 +extern void reset_revision_walk(void);
  extern int prepare_revision_walk(struct rev_info *revs);
  extern struct commit *get_revision(struct rev_info *revs);
  extern char *get_revision_mark(const struct rev_info *revs, const struct commit *commit);
diff --combined setup.c
index e11497720e01dd14a66e152883e675669fc3b548,233bfbe9203058d4091a42fa8fd5231f0285c839..9139beefc75c4fe92cf8c7dc54a9fae972995aca
+++ b/setup.c
@@@ -4,7 -4,7 +4,7 @@@
  static int inside_git_dir = -1;
  static int inside_work_tree = -1;
  
 -char *prefix_path(const char *prefix, int len, const char *path)
 +static char *prefix_path_gently(const char *prefix, int len, const char *path)
  {
        const char *orig = path;
        char *sanitized;
@@@ -31,8 -31,7 +31,8 @@@
                if (strncmp(sanitized, work_tree, len) ||
                    (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
                error_out:
 -                      die("'%s' is outside repository", orig);
 +                      free(sanitized);
 +                      return NULL;
                }
                if (sanitized[len] == '/')
                        len++;
        return sanitized;
  }
  
 +char *prefix_path(const char *prefix, int len, const char *path)
 +{
 +      char *r = prefix_path_gently(prefix, len, path);
 +      if (!r)
 +              die("'%s' is outside repository", path);
 +      return r;
 +}
 +
 +int path_inside_repo(const char *prefix, const char *path)
 +{
 +      int len = prefix ? strlen(prefix) : 0;
 +      char *r = prefix_path_gently(prefix, len, path);
 +      if (r) {
 +              free(r);
 +              return 1;
 +      }
 +      return 0;
 +}
 +
  int check_filename(const char *prefix, const char *arg)
  {
        const char *name;
        die_errno("failed to stat '%s'", arg);
  }
  
 -static void NORETURN die_verify_filename(const char *prefix, const char *arg)
 +static void NORETURN die_verify_filename(const char *prefix,
 +                                       const char *arg,
 +                                       int diagnose_misspelt_rev)
  {
-       unsigned char sha1[20];
-       unsigned mode;
 +      if (!diagnose_misspelt_rev)
 +              die("%s: no such path in the working tree.\n"
 +                  "Use '-- <path>...' to specify paths that do not exist locally.",
 +                  arg);
        /*
         * Saying "'(icase)foo' does not exist in the index" when the
         * user gave us ":(icase)foo" is just stupid.  A magic pathspec
         * begins with a colon and is followed by a non-alnum; do not
-        * let get_sha1_with_mode_1(only_to_die=1) to even trigger.
+        * let maybe_die_on_misspelt_object_name() even trigger.
         */
        if (!(arg[0] == ':' && !isalnum(arg[1])))
-               /* try a detailed diagnostic ... */
-               get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
+               maybe_die_on_misspelt_object_name(arg, prefix);
  
        /* ... or fall back the most general message. */
        die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
   * as true, because even if such a filename were to exist, we want
   * it to be preceded by the "--" marker (or we want the user to
   * use a format like "./-filename")
 + *
 + * The "diagnose_misspelt_rev" is used to provide a user-friendly
 + * diagnosis when dying upon finding that "name" is not a pathname.
 + * If set to 1, the diagnosis will try to diagnose "name" as an
 + * invalid object name (e.g. HEAD:foo). If set to 0, the diagnosis
 + * will only complain about an inexisting file.
 + *
 + * This function is typically called to check that a "file or rev"
 + * argument is unambiguous. In this case, the caller will want
 + * diagnose_misspelt_rev == 1 when verifying the first non-rev
 + * argument (which could have been a revision), and
 + * diagnose_misspelt_rev == 0 for the next ones (because we already
 + * saw a filename, there's not ambiguity anymore).
   */
 -void verify_filename(const char *prefix, const char *arg)
 +void verify_filename(const char *prefix,
 +                   const char *arg,
 +                   int diagnose_misspelt_rev)
  {
        if (*arg == '-')
                die("bad flag '%s' used after filename", arg);
        if (check_filename(prefix, arg))
                return;
 -      die_verify_filename(prefix, arg);
 +      die_verify_filename(prefix, arg, diagnose_misspelt_rev);
  }
  
  /*
@@@ -288,7 -243,7 +284,7 @@@ const char **get_pathspec(const char *p
   *    a proper "ref:", or a regular file HEAD that has a properly
   *    formatted sha1 object name.
   */
 -static int is_git_directory(const char *suspect)
 +int is_git_directory(const char *suspect)
  {
        char path[PATH_MAX];
        size_t len = strlen(suspect);
@@@ -610,15 -565,13 +606,15 @@@ static const char *setup_nongit(const c
        return NULL;
  }
  
 -static dev_t get_device_or_die(const char *path, const char *prefix)
 +static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len)
  {
        struct stat buf;
 -      if (stat(path, &buf))
 -              die_errno("failed to stat '%s%s%s'",
 +      if (stat(path, &buf)) {
 +              die_errno("failed to stat '%*s%s%s'",
 +                              prefix_len,
                                prefix ? prefix : "",
                                prefix ? "/" : "", path);
 +      }
        return buf.st_dev;
  }
  
@@@ -632,7 -585,7 +628,7 @@@ static const char *setup_git_directory_
        static char cwd[PATH_MAX+1];
        const char *gitdirenv, *ret;
        char *gitfile;
 -      int len, offset, ceil_offset;
 +      int len, offset, offset_parent, ceil_offset;
        dev_t current_device = 0;
        int one_filesystem = 1;
  
         */
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
        if (one_filesystem)
 -              current_device = get_device_or_die(".", NULL);
 +              current_device = get_device_or_die(".", NULL, 0);
        for (;;) {
                gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
                if (gitfile)
                if (is_git_directory("."))
                        return setup_bare_git_dir(cwd, offset, len, nongit_ok);
  
 -              while (--offset > ceil_offset && cwd[offset] != '/');
 -              if (offset <= ceil_offset)
 +              offset_parent = offset;
 +              while (--offset_parent > ceil_offset && cwd[offset_parent] != '/');
 +              if (offset_parent <= ceil_offset)
                        return setup_nongit(cwd, nongit_ok);
                if (one_filesystem) {
 -                      dev_t parent_device = get_device_or_die("..", cwd);
 +                      dev_t parent_device = get_device_or_die("..", cwd, offset);
                        if (parent_device != current_device) {
                                if (nongit_ok) {
                                        if (chdir(cwd))
                                        return NULL;
                                }
                                cwd[offset] = '\0';
 -                              die("Not a git repository (or any parent up to mount parent %s)\n"
 +                              die("Not a git repository (or any parent up to mount point %s)\n"
                                "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
                        }
                }
                        cwd[offset] = '\0';
                        die_errno("Cannot change to '%s/..'", cwd);
                }
 +              offset = offset_parent;
        }
  }
  
diff --combined sha1_name.c
index 5d81ea0564c8d90b417eb8ec5ed8c32baa2c3ea3,8bc20c54b17d4f1c08b4c7f8d8f38bb6a9e25011..95003c77ea9a0ee240d60bc7958bb48245b14ad4
@@@ -9,14 -9,82 +9,82 @@@
  
  static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
  
- static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
+ typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
+ struct disambiguate_state {
+       disambiguate_hint_fn fn;
+       void *cb_data;
+       unsigned char candidate[20];
+       unsigned candidate_exists:1;
+       unsigned candidate_checked:1;
+       unsigned candidate_ok:1;
+       unsigned disambiguate_fn_used:1;
+       unsigned ambiguous:1;
+       unsigned always_call_fn:1;
+ };
+ static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
+ {
+       if (ds->always_call_fn) {
+               ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
+               return;
+       }
+       if (!ds->candidate_exists) {
+               /* this is the first candidate */
+               hashcpy(ds->candidate, current);
+               ds->candidate_exists = 1;
+               return;
+       } else if (!hashcmp(ds->candidate, current)) {
+               /* the same as what we already have seen */
+               return;
+       }
+       if (!ds->fn) {
+               /* cannot disambiguate between ds->candidate and current */
+               ds->ambiguous = 1;
+               return;
+       }
+       if (!ds->candidate_checked) {
+               ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
+               ds->disambiguate_fn_used = 1;
+               ds->candidate_checked = 1;
+       }
+       if (!ds->candidate_ok) {
+               /* discard the candidate; we know it does not satisify fn */
+               hashcpy(ds->candidate, current);
+               ds->candidate_checked = 0;
+               return;
+       }
+       /* if we reach this point, we know ds->candidate satisfies fn */
+       if (ds->fn(current, ds->cb_data)) {
+               /*
+                * if both current and candidate satisfy fn, we cannot
+                * disambiguate.
+                */
+               ds->candidate_ok = 0;
+               ds->ambiguous = 1;
+       }
+       /* otherwise, current can be discarded and candidate is still good */
+ }
+ static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
  {
        struct alternate_object_database *alt;
        char hex[40];
-       int found = 0;
        static struct alternate_object_database *fakeent;
  
        if (!fakeent) {
+               /*
+                * Create a "fake" alternate object database that
+                * points to our own object database, to make it
+                * easier to get a temporary working space in
+                * alt->name/alt->base while iterating over the
+                * object databases including our own.
+                */
                const char *objdir = get_object_directory();
                int objdir_len = strlen(objdir);
                int entlen = objdir_len + 43;
        }
        fakeent->next = alt_odb_list;
  
-       sprintf(hex, "%.2s", name);
-       for (alt = fakeent; alt && found < 2; alt = alt->next) {
+       sprintf(hex, "%.2s", hex_pfx);
+       for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
                struct dirent *de;
                DIR *dir;
-               sprintf(alt->name, "%.2s/", name);
+               sprintf(alt->name, "%.2s/", hex_pfx);
                dir = opendir(alt->base);
                if (!dir)
                        continue;
-               while ((de = readdir(dir)) != NULL) {
+               while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
+                       unsigned char sha1[20];
                        if (strlen(de->d_name) != 38)
                                continue;
-                       if (memcmp(de->d_name, name + 2, len - 2))
+                       if (memcmp(de->d_name, hex_pfx + 2, len - 2))
                                continue;
-                       if (!found) {
-                               memcpy(hex + 2, de->d_name, 38);
-                               found++;
-                       }
-                       else if (memcmp(hex + 2, de->d_name, 38)) {
-                               found = 2;
-                               break;
-                       }
+                       memcpy(hex + 2, de->d_name, 38);
+                       if (!get_sha1_hex(hex, sha1))
+                               update_candidates(ds, sha1);
                }
                closedir(dir);
        }
-       if (found == 1)
-               return get_sha1_hex(hex, sha1) == 0;
-       return found;
  }
  
  static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
        return 1;
  }
  
- static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
+ static void unique_in_pack(int len,
+                         const unsigned char *bin_pfx,
+                          struct packed_git *p,
+                          struct disambiguate_state *ds)
  {
-       struct packed_git *p;
-       const unsigned char *found_sha1 = NULL;
-       int found = 0;
-       prepare_packed_git();
-       for (p = packed_git; p && found < 2; p = p->next) {
-               uint32_t num, last;
-               uint32_t first = 0;
-               open_pack_index(p);
-               num = p->num_objects;
-               last = num;
-               while (first < last) {
-                       uint32_t mid = (first + last) / 2;
-                       const unsigned char *now;
-                       int cmp;
-                       now = nth_packed_object_sha1(p, mid);
-                       cmp = hashcmp(match, now);
-                       if (!cmp) {
-                               first = mid;
-                               break;
-                       }
-                       if (cmp > 0) {
-                               first = mid+1;
-                               continue;
-                       }
-                       last = mid;
+       uint32_t num, last, i, first = 0;
+       const unsigned char *current = NULL;
+       open_pack_index(p);
+       num = p->num_objects;
+       last = num;
+       while (first < last) {
+               uint32_t mid = (first + last) / 2;
+               const unsigned char *current;
+               int cmp;
+               current = nth_packed_object_sha1(p, mid);
+               cmp = hashcmp(bin_pfx, current);
+               if (!cmp) {
+                       first = mid;
+                       break;
                }
-               if (first < num) {
-                       const unsigned char *now, *next;
-                      now = nth_packed_object_sha1(p, first);
-                       if (match_sha(len, match, now)) {
-                               next = nth_packed_object_sha1(p, first+1);
-                              if (!next|| !match_sha(len, match, next)) {
-                                       /* unique within this pack */
-                                       if (!found) {
-                                               found_sha1 = now;
-                                               found++;
-                                       }
-                                       else if (hashcmp(found_sha1, now)) {
-                                               found = 2;
-                                               break;
-                                       }
-                               }
-                               else {
-                                       /* not even unique within this pack */
-                                       found = 2;
-                                       break;
-                               }
-                       }
+               if (cmp > 0) {
+                       first = mid+1;
+                       continue;
                }
+               last = mid;
+       }
+       /*
+        * At this point, "first" is the location of the lowest object
+        * with an object name that could match "bin_pfx".  See if we have
+        * 0, 1 or more objects that actually match(es).
+        */
+       for (i = first; i < num && !ds->ambiguous; i++) {
+               current = nth_packed_object_sha1(p, i);
+               if (!match_sha(len, bin_pfx, current))
+                       break;
+               update_candidates(ds, current);
        }
-       if (found == 1)
-               hashcpy(sha1, found_sha1);
-       return found;
+ }
+ static void find_short_packed_object(int len, const unsigned char *bin_pfx,
+                                    struct disambiguate_state *ds)
+ {
+       struct packed_git *p;
+       prepare_packed_git();
+       for (p = packed_git; p && !ds->ambiguous; p = p->next)
+               unique_in_pack(len, bin_pfx, p, ds);
  }
  
  #define SHORT_NAME_NOT_FOUND (-1)
  #define SHORT_NAME_AMBIGUOUS (-2)
  
- static int find_unique_short_object(int len, char *canonical,
-                                   unsigned char *res, unsigned char *sha1)
+ static int finish_object_disambiguation(struct disambiguate_state *ds,
+                                       unsigned char *sha1)
  {
-       int has_unpacked, has_packed;
-       unsigned char unpacked_sha1[20], packed_sha1[20];
+       if (ds->ambiguous)
+               return SHORT_NAME_AMBIGUOUS;
  
-       prepare_alt_odb();
-       has_unpacked = find_short_object_filename(len, canonical, unpacked_sha1);
-       has_packed = find_short_packed_object(len, res, packed_sha1);
-       if (!has_unpacked && !has_packed)
+       if (!ds->candidate_exists)
                return SHORT_NAME_NOT_FOUND;
-       if (1 < has_unpacked || 1 < has_packed)
+       if (!ds->candidate_checked)
+               /*
+                * If this is the only candidate, there is no point
+                * calling the disambiguation hint callback.
+                *
+                * On the other hand, if the current candidate
+                * replaced an earlier candidate that did _not_ pass
+                * the disambiguation hint callback, then we do have
+                * more than one objects that match the short name
+                * given, so we should make sure this one matches;
+                * otherwise, if we discovered this one and the one
+                * that we previously discarded in the reverse order,
+                * we would end up showing different results in the
+                * same repository!
+                */
+               ds->candidate_ok = (!ds->disambiguate_fn_used ||
+                                   ds->fn(ds->candidate, ds->cb_data));
+       if (!ds->candidate_ok)
                return SHORT_NAME_AMBIGUOUS;
-       if (has_unpacked != has_packed) {
-               hashcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1));
+       hashcpy(sha1, ds->candidate);
+       return 0;
+ }
+ static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
+ {
+       int kind = sha1_object_info(sha1, NULL);
+       return kind == OBJ_COMMIT;
+ }
+ static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
+ {
+       struct object *obj;
+       int kind;
+       kind = sha1_object_info(sha1, NULL);
+       if (kind == OBJ_COMMIT)
+               return 1;
+       if (kind != OBJ_TAG)
                return 0;
-       }
-       /* Both have unique ones -- do they match? */
-       if (hashcmp(packed_sha1, unpacked_sha1))
-               return SHORT_NAME_AMBIGUOUS;
-       hashcpy(sha1, packed_sha1);
+       /* We need to do this the hard way... */
+       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       if (obj && obj->type == OBJ_COMMIT)
+               return 1;
        return 0;
  }
  
- static int get_short_sha1(const char *name, int len, unsigned char *sha1,
-                         int quietly)
+ static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
  {
-       int i, status;
-       char canonical[40];
-       unsigned char res[20];
+       int kind = sha1_object_info(sha1, NULL);
+       return kind == OBJ_TREE;
+ }
  
-       if (len < MINIMUM_ABBREV || len > 40)
-               return -1;
-       hashclr(res);
-       memset(canonical, 'x', 40);
+ static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
+ {
+       struct object *obj;
+       int kind;
+       kind = sha1_object_info(sha1, NULL);
+       if (kind == OBJ_TREE || kind == OBJ_COMMIT)
+               return 1;
+       if (kind != OBJ_TAG)
+               return 0;
+       /* We need to do this the hard way... */
+       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
+               return 1;
+       return 0;
+ }
+ static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
+ {
+       int kind = sha1_object_info(sha1, NULL);
+       return kind == OBJ_BLOB;
+ }
+ static int prepare_prefixes(const char *name, int len,
+                           unsigned char *bin_pfx,
+                           char *hex_pfx)
+ {
+       int i;
+       hashclr(bin_pfx);
+       memset(hex_pfx, 'x', 40);
        for (i = 0; i < len ;i++) {
                unsigned char c = name[i];
                unsigned char val;
                }
                else
                        return -1;
-               canonical[i] = c;
+               hex_pfx[i] = c;
                if (!(i & 1))
                        val <<= 4;
-               res[i >> 1] |= val;
+               bin_pfx[i >> 1] |= val;
        }
+       return 0;
+ }
+ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
+                         unsigned flags)
+ {
+       int status;
+       char hex_pfx[40];
+       unsigned char bin_pfx[20];
+       struct disambiguate_state ds;
+       int quietly = !!(flags & GET_SHA1_QUIETLY);
+       if (len < MINIMUM_ABBREV || len > 40)
+               return -1;
+       if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
+               return -1;
+       prepare_alt_odb();
+       memset(&ds, 0, sizeof(ds));
+       if (flags & GET_SHA1_COMMIT)
+               ds.fn = disambiguate_commit_only;
+       else if (flags & GET_SHA1_COMMITTISH)
+               ds.fn = disambiguate_committish_only;
+       else if (flags & GET_SHA1_TREE)
+               ds.fn = disambiguate_tree_only;
+       else if (flags & GET_SHA1_TREEISH)
+               ds.fn = disambiguate_treeish_only;
+       else if (flags & GET_SHA1_BLOB)
+               ds.fn = disambiguate_blob_only;
+       find_short_object_filename(len, hex_pfx, &ds);
+       find_short_packed_object(len, bin_pfx, &ds);
+       status = finish_object_disambiguation(&ds, sha1);
  
-       status = find_unique_short_object(i, canonical, res, sha1);
        if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
-               return error("short SHA1 %.*s is ambiguous.", len, canonical);
+               return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
        return status;
  }
  
+ int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
+ {
+       char hex_pfx[40];
+       unsigned char bin_pfx[20];
+       struct disambiguate_state ds;
+       int len = strlen(prefix);
+       if (len < MINIMUM_ABBREV || len > 40)
+               return -1;
+       if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
+               return -1;
+       prepare_alt_odb();
+       memset(&ds, 0, sizeof(ds));
+       ds.always_call_fn = 1;
+       ds.cb_data = cb_data;
+       ds.fn = fn;
+       find_short_object_filename(len, hex_pfx, &ds);
+       find_short_packed_object(len, bin_pfx, &ds);
+       return ds.ambiguous;
+ }
  const char *find_unique_abbrev(const unsigned char *sha1, int len)
  {
        int status, exists;
                return hex;
        while (len < 40) {
                unsigned char sha1_ret[20];
-               status = get_short_sha1(hex, len, sha1_ret, 1);
+               status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
                if (exists
                    ? !status
                    : status == SHORT_NAME_NOT_FOUND) {
@@@ -255,7 -430,7 +430,7 @@@ static inline int upstream_mark(const c
        return 0;
  }
  
- static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
  
  static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
  {
                ret = interpret_branch_name(str+at, &buf);
                if (ret > 0) {
                        /* substitute this branch name and restart */
-                       return get_sha1_1(buf.buf, buf.len, sha1);
+                       return get_sha1_1(buf.buf, buf.len, sha1, 0);
                } else if (ret == 0) {
                        return -1;
                }
@@@ -362,7 -537,7 +537,7 @@@ static int get_parent(const char *name
                      unsigned char *result, int idx)
  {
        unsigned char sha1[20];
-       int ret = get_sha1_1(name, len, sha1);
+       int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
  
@@@ -395,7 -570,7 +570,7 @@@ static int get_nth_ancestor(const char 
        struct commit *commit;
        int ret;
  
-       ret = get_sha1_1(name, len, sha1);
+       ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
        if (ret)
                return ret;
        commit = lookup_commit_reference(sha1);
@@@ -441,6 -616,7 +616,7 @@@ static int peel_onion(const char *name
        unsigned char outer[20];
        const char *sp;
        unsigned int expected_type = 0;
+       unsigned lookup_flags = 0;
        struct object *o;
  
        /*
        else
                return -1;
  
-       if (get_sha1_1(name, sp - name - 2, outer))
+       if (expected_type == OBJ_COMMIT)
+               lookup_flags = GET_SHA1_COMMITTISH;
+       if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
                return -1;
  
        o = parse_object(outer);
  static int get_describe_name(const char *name, int len, unsigned char *sha1)
  {
        const char *cp;
+       unsigned flags = GET_SHA1_QUIETLY | GET_SHA1_COMMIT;
  
        for (cp = name + len - 1; name + 2 <= cp; cp--) {
                char ch = *cp;
                        if (ch == 'g' && cp[-1] == '-') {
                                cp++;
                                len -= cp - name;
-                               return get_short_sha1(cp, len, sha1, 1);
+                               return get_short_sha1(cp, len, sha1, flags);
                        }
                }
        }
        return -1;
  }
  
- static int get_sha1_1(const char *name, int len, unsigned char *sha1)
+ static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags)
  {
        int ret, has_suffix;
        const char *cp;
        if (!ret)
                return 0;
  
-       return get_short_sha1(name, len, sha1, 0);
+       return get_short_sha1(name, len, sha1, lookup_flags);
  }
  
  /*
@@@ -769,7 -949,7 +949,7 @@@ int get_sha1_mb(const char *name, unsig
                struct strbuf sb;
                strbuf_init(&sb, dots - name);
                strbuf_add(&sb, name, dots - name);
-               st = get_sha1(sb.buf, sha1_tmp);
+               st = get_sha1_committish(sb.buf, sha1_tmp);
                strbuf_release(&sb);
        }
        if (st)
        if (!one)
                return -1;
  
-       if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+       if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
                return -1;
        two = lookup_commit_reference_gently(sha1_tmp, 0);
        if (!two)
@@@ -856,22 -1036,10 +1036,22 @@@ int interpret_branch_name(const char *n
        len = cp + tmp_len - name;
        cp = xstrndup(name, cp - name);
        upstream = branch_get(*cp ? cp : NULL);
 -      if (!upstream
 -          || !upstream->merge
 -          || !upstream->merge[0]->dst)
 -              return error("No upstream branch found for '%s'", cp);
 +      /*
 +       * Upstream can be NULL only if cp refers to HEAD and HEAD
 +       * points to something different than a branch.
 +       */
 +      if (!upstream)
 +              return error(_("HEAD does not point to a branch"));
 +      if (!upstream->merge || !upstream->merge[0]->dst) {
 +              if (!ref_exists(upstream->refname))
 +                      return error(_("No such branch: '%s'"), cp);
 +              if (!upstream->merge)
 +                      return error(_("No upstream configured for branch '%s'"),
 +                                   upstream->name);
 +              return error(
 +                      _("Upstream branch '%s' not stored as a remote-tracking branch"),
 +                      upstream->merge[0]->src);
 +      }
        free(cp);
        cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
        strbuf_reset(buf);
@@@ -905,7 -1073,52 +1085,52 @@@ int strbuf_check_branch_ref(struct strb
  int get_sha1(const char *name, unsigned char *sha1)
  {
        struct object_context unused;
-       return get_sha1_with_context(name, sha1, &unused);
+       return get_sha1_with_context(name, 0, sha1, &unused);
+ }
+ /*
+  * Many callers know that the user meant to name a committish by
+  * syntactical positions where the object name appears.  Calling this
+  * function allows the machinery to disambiguate shorter-than-unique
+  * abbreviated object names between committish and others.
+  *
+  * Note that this does NOT error out when the named object is not a
+  * committish. It is merely to give a hint to the disambiguation
+  * machinery.
+  */
+ int get_sha1_committish(const char *name, unsigned char *sha1)
+ {
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_COMMITTISH,
+                                    sha1, &unused);
+ }
+ int get_sha1_treeish(const char *name, unsigned char *sha1)
+ {
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_TREEISH,
+                                    sha1, &unused);
+ }
+ int get_sha1_commit(const char *name, unsigned char *sha1)
+ {
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_COMMIT,
+                                    sha1, &unused);
+ }
+ int get_sha1_tree(const char *name, unsigned char *sha1)
+ {
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_TREE,
+                                    sha1, &unused);
+ }
+ int get_sha1_blob(const char *name, unsigned char *sha1)
+ {
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_BLOB,
+                                    sha1, &unused);
  }
  
  /* Must be called only when object_name:filename doesn't exist. */
@@@ -1004,16 -1217,6 +1229,6 @@@ static void diagnose_invalid_index_path
  }
  
  
- int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
-                        int only_to_die, const char *prefix)
- {
-       struct object_context oc;
-       int ret;
-       ret = get_sha1_with_context_1(name, sha1, &oc, only_to_die, prefix);
-       *mode = oc.mode;
-       return ret;
- }
  static char *resolve_relative_path(const char *rel)
  {
        if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
                           rel);
  }
  
- int get_sha1_with_context_1(const char *name, unsigned char *sha1,
-                           struct object_context *oc,
-                           int only_to_die, const char *prefix)
+ static int get_sha1_with_context_1(const char *name,
+                                  unsigned flags,
+                                  const char *prefix,
+                                  unsigned char *sha1,
+                                  struct object_context *oc)
  {
        int ret, bracket_depth;
        int namelen = strlen(name);
        const char *cp;
+       int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
  
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
-       ret = get_sha1_1(name, namelen, sha1);
+       ret = get_sha1_1(name, namelen, sha1, flags);
        if (!ret)
                return ret;
-       /* sha1:path --> object name of path in ent sha1
+       /*
+        * sha1:path --> object name of path in ent sha1
         * :path -> object name of absolute path in index
         * :./path -> object name of path relative to cwd in index
         * :[0-3]:path -> object name of path in index at stage
                        strncpy(object_name, name, cp-name);
                        object_name[cp-name] = '\0';
                }
-               if (!get_sha1_1(name, cp-name, tree_sha1)) {
+               if (!get_sha1_1(name, cp-name, tree_sha1, GET_SHA1_TREEISH)) {
                        const char *filename = cp+1;
                        char *new_filename = NULL;
  
                        if (new_filename)
                                filename = new_filename;
                        ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
 -                      if (only_to_die) {
 +                      if (ret && only_to_die) {
                                diagnose_invalid_sha1_path(prefix, filename,
                                                           tree_sha1, object_name);
                                free(object_name);
        }
        return ret;
  }
+ /*
+  * Call this function when you know "name" given by the end user must
+  * name an object but it doesn't; the function _may_ die with a better
+  * diagnostic message than "no such object 'name'", e.g. "Path 'doc' does not
+  * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
+  * you have a chance to diagnose the error further.
+  */
+ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
+ {
+       struct object_context oc;
+       unsigned char sha1[20];
+       get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
+ }
+ int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
+ {
+       return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
+ }