Merge branch 'bw/object-id'
authorJunio C Hamano <gitster@pobox.com>
Mon, 19 Jun 2017 19:38:44 +0000 (12:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 19 Jun 2017 19:38:44 +0000 (12:38 -0700)
Conversion from uchar[20] to struct object_id continues.

* bw/object-id: (33 commits)
diff: rename diff_fill_sha1_info to diff_fill_oid_info
diffcore-rename: use is_empty_blob_oid
tree-diff: convert path_appendnew to object_id
tree-diff: convert diff_tree_paths to struct object_id
tree-diff: convert try_to_follow_renames to struct object_id
builtin/diff-tree: cleanup references to sha1
diff-tree: convert diff_tree_sha1 to struct object_id
notes-merge: convert write_note_to_worktree to struct object_id
notes-merge: convert verify_notes_filepair to struct object_id
notes-merge: convert find_notes_merge_pair_ps to struct object_id
notes-merge: convert merge_from_diffs to struct object_id
notes-merge: convert notes_merge* to struct object_id
tree-diff: convert diff_root_tree_sha1 to struct object_id
combine-diff: convert find_paths_* to struct object_id
combine-diff: convert diff_tree_combined to struct object_id
diff: convert diff_flush_patch_id to struct object_id
patch-ids: convert to struct object_id
diff: finish conversion for prepare_temp_file to struct object_id
diff: convert reuse_worktree_file to struct object_id
diff: convert fill_filespec to struct object_id
...

20 files changed:
1  2 
blame.c
builtin/am.c
builtin/commit.c
builtin/diff-tree.c
builtin/diff.c
builtin/fast-export.c
builtin/grep.c
builtin/log.c
builtin/merge.c
builtin/notes.c
cache.h
diff-lib.c
diff.c
diff.h
grep.c
grep.h
notes-utils.c
remote-testsvn.c
revision.c
sequencer.c
diff --combined blame.c
index 843c845cbaf6bc9d60ec8dcf79bad1ce0669319c,0000000000000000000000000000000000000000..194b58e96066f9620005f64a57ca9ecc618c86f4
mode 100644,000000..100644
--- /dev/null
+++ b/blame.c
@@@ -1,1863 -1,0 +1,1863 @@@
-               diff_tree_sha1(parent->tree->object.oid.hash,
-                              origin->commit->tree->object.oid.hash,
-                              "", &diff_opts);
 +#include "cache.h"
 +#include "refs.h"
 +#include "cache-tree.h"
 +#include "mergesort.h"
 +#include "diff.h"
 +#include "diffcore.h"
 +#include "tag.h"
 +#include "blame.h"
 +
 +void blame_origin_decref(struct blame_origin *o)
 +{
 +      if (o && --o->refcnt <= 0) {
 +              struct blame_origin *p, *l = NULL;
 +              if (o->previous)
 +                      blame_origin_decref(o->previous);
 +              free(o->file.ptr);
 +              /* Should be present exactly once in commit chain */
 +              for (p = o->commit->util; p; l = p, p = p->next) {
 +                      if (p == o) {
 +                              if (l)
 +                                      l->next = p->next;
 +                              else
 +                                      o->commit->util = p->next;
 +                              free(o);
 +                              return;
 +                      }
 +              }
 +              die("internal error in blame_origin_decref");
 +      }
 +}
 +
 +/*
 + * Given a commit and a path in it, create a new origin structure.
 + * The callers that add blame to the scoreboard should use
 + * get_origin() to obtain shared, refcounted copy instead of calling
 + * this function directly.
 + */
 +static struct blame_origin *make_origin(struct commit *commit, const char *path)
 +{
 +      struct blame_origin *o;
 +      FLEX_ALLOC_STR(o, path, path);
 +      o->commit = commit;
 +      o->refcnt = 1;
 +      o->next = commit->util;
 +      commit->util = o;
 +      return o;
 +}
 +
 +/*
 + * Locate an existing origin or create a new one.
 + * This moves the origin to front position in the commit util list.
 + */
 +static struct blame_origin *get_origin(struct commit *commit, const char *path)
 +{
 +      struct blame_origin *o, *l;
 +
 +      for (o = commit->util, l = NULL; o; l = o, o = o->next) {
 +              if (!strcmp(o->path, path)) {
 +                      /* bump to front */
 +                      if (l) {
 +                              l->next = o->next;
 +                              o->next = commit->util;
 +                              commit->util = o;
 +                      }
 +                      return blame_origin_incref(o);
 +              }
 +      }
 +      return make_origin(commit, path);
 +}
 +
 +
 +
 +static void verify_working_tree_path(struct commit *work_tree, const char *path)
 +{
 +      struct commit_list *parents;
 +      int pos;
 +
 +      for (parents = work_tree->parents; parents; parents = parents->next) {
 +              const struct object_id *commit_oid = &parents->item->object.oid;
 +              struct object_id blob_oid;
 +              unsigned mode;
 +
 +              if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
 +                  sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
 +                      return;
 +      }
 +
 +      pos = cache_name_pos(path, strlen(path));
 +      if (pos >= 0)
 +              ; /* path is in the index */
 +      else if (-1 - pos < active_nr &&
 +               !strcmp(active_cache[-1 - pos]->name, path))
 +              ; /* path is in the index, unmerged */
 +      else
 +              die("no such path '%s' in HEAD", path);
 +}
 +
 +static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
 +{
 +      struct commit *parent;
 +
 +      parent = lookup_commit_reference(oid);
 +      if (!parent)
 +              die("no such commit %s", oid_to_hex(oid));
 +      return &commit_list_insert(parent, tail)->next;
 +}
 +
 +static void append_merge_parents(struct commit_list **tail)
 +{
 +      int merge_head;
 +      struct strbuf line = STRBUF_INIT;
 +
 +      merge_head = open(git_path_merge_head(), O_RDONLY);
 +      if (merge_head < 0) {
 +              if (errno == ENOENT)
 +                      return;
 +              die("cannot open '%s' for reading", git_path_merge_head());
 +      }
 +
 +      while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
 +              struct object_id oid;
 +              if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
 +                      die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
 +              tail = append_parent(tail, &oid);
 +      }
 +      close(merge_head);
 +      strbuf_release(&line);
 +}
 +
 +/*
 + * This isn't as simple as passing sb->buf and sb->len, because we
 + * want to transfer ownership of the buffer to the commit (so we
 + * must use detach).
 + */
 +static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
 +{
 +      size_t len;
 +      void *buf = strbuf_detach(sb, &len);
 +      set_commit_buffer(c, buf, len);
 +}
 +
 +/*
 + * Prepare a dummy commit that represents the work tree (or staged) item.
 + * Note that annotating work tree item never works in the reverse.
 + */
 +static struct commit *fake_working_tree_commit(struct diff_options *opt,
 +                                             const char *path,
 +                                             const char *contents_from)
 +{
 +      struct commit *commit;
 +      struct blame_origin *origin;
 +      struct commit_list **parent_tail, *parent;
 +      struct object_id head_oid;
 +      struct strbuf buf = STRBUF_INIT;
 +      const char *ident;
 +      time_t now;
 +      int size, len;
 +      struct cache_entry *ce;
 +      unsigned mode;
 +      struct strbuf msg = STRBUF_INIT;
 +
 +      read_cache();
 +      time(&now);
 +      commit = alloc_commit_node();
 +      commit->object.parsed = 1;
 +      commit->date = now;
 +      parent_tail = &commit->parents;
 +
 +      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
 +              die("no such ref: HEAD");
 +
 +      parent_tail = append_parent(parent_tail, &head_oid);
 +      append_merge_parents(parent_tail);
 +      verify_working_tree_path(commit, path);
 +
 +      origin = make_origin(commit, path);
 +
 +      ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
 +      strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
 +      for (parent = commit->parents; parent; parent = parent->next)
 +              strbuf_addf(&msg, "parent %s\n",
 +                          oid_to_hex(&parent->item->object.oid));
 +      strbuf_addf(&msg,
 +                  "author %s\n"
 +                  "committer %s\n\n"
 +                  "Version of %s from %s\n",
 +                  ident, ident, path,
 +                  (!contents_from ? path :
 +                   (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
 +      set_commit_buffer_from_strbuf(commit, &msg);
 +
 +      if (!contents_from || strcmp("-", contents_from)) {
 +              struct stat st;
 +              const char *read_from;
 +              char *buf_ptr;
 +              unsigned long buf_len;
 +
 +              if (contents_from) {
 +                      if (stat(contents_from, &st) < 0)
 +                              die_errno("Cannot stat '%s'", contents_from);
 +                      read_from = contents_from;
 +              }
 +              else {
 +                      if (lstat(path, &st) < 0)
 +                              die_errno("Cannot lstat '%s'", path);
 +                      read_from = path;
 +              }
 +              mode = canon_mode(st.st_mode);
 +
 +              switch (st.st_mode & S_IFMT) {
 +              case S_IFREG:
 +                      if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 +                          textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
 +                              strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
 +                      else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
 +                              die_errno("cannot open or read '%s'", read_from);
 +                      break;
 +              case S_IFLNK:
 +                      if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
 +                              die_errno("cannot readlink '%s'", read_from);
 +                      break;
 +              default:
 +                      die("unsupported file type %s", read_from);
 +              }
 +      }
 +      else {
 +              /* Reading from stdin */
 +              mode = 0;
 +              if (strbuf_read(&buf, 0, 0) < 0)
 +                      die_errno("failed to read from stdin");
 +      }
 +      convert_to_git(path, buf.buf, buf.len, &buf, 0);
 +      origin->file.ptr = buf.buf;
 +      origin->file.size = buf.len;
 +      pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
 +
 +      /*
 +       * Read the current index, replace the path entry with
 +       * origin->blob_sha1 without mucking with its mode or type
 +       * bits; we are not going to write this index out -- we just
 +       * want to run "diff-index --cached".
 +       */
 +      discard_cache();
 +      read_cache();
 +
 +      len = strlen(path);
 +      if (!mode) {
 +              int pos = cache_name_pos(path, len);
 +              if (0 <= pos)
 +                      mode = active_cache[pos]->ce_mode;
 +              else
 +                      /* Let's not bother reading from HEAD tree */
 +                      mode = S_IFREG | 0644;
 +      }
 +      size = cache_entry_size(len);
 +      ce = xcalloc(1, size);
 +      oidcpy(&ce->oid, &origin->blob_oid);
 +      memcpy(ce->name, path, len);
 +      ce->ce_flags = create_ce_flags(0);
 +      ce->ce_namelen = len;
 +      ce->ce_mode = create_ce_mode(mode);
 +      add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 +
 +      cache_tree_invalidate_path(&the_index, path);
 +
 +      return commit;
 +}
 +
 +
 +
 +static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
 +                    xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)
 +{
 +      xpparam_t xpp = {0};
 +      xdemitconf_t xecfg = {0};
 +      xdemitcb_t ecb = {NULL};
 +
 +      xpp.flags = xdl_opts;
 +      xecfg.hunk_func = hunk_func;
 +      ecb.priv = cb_data;
 +      return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
 +}
 +
 +/*
 + * Given an origin, prepare mmfile_t structure to be used by the
 + * diff machinery
 + */
 +static void fill_origin_blob(struct diff_options *opt,
 +                           struct blame_origin *o, mmfile_t *file, int *num_read_blob)
 +{
 +      if (!o->file.ptr) {
 +              enum object_type type;
 +              unsigned long file_size;
 +
 +              (*num_read_blob)++;
 +              if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 +                  textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
 +                      ;
 +              else
 +                      file->ptr = read_sha1_file(o->blob_oid.hash, &type,
 +                                                 &file_size);
 +              file->size = file_size;
 +
 +              if (!file->ptr)
 +                      die("Cannot read blob %s for path %s",
 +                          oid_to_hex(&o->blob_oid),
 +                          o->path);
 +              o->file = *file;
 +      }
 +      else
 +              *file = o->file;
 +}
 +
 +static void drop_origin_blob(struct blame_origin *o)
 +{
 +      if (o->file.ptr) {
 +              free(o->file.ptr);
 +              o->file.ptr = NULL;
 +      }
 +}
 +
 +/*
 + * Any merge of blames happens on lists of blames that arrived via
 + * different parents in a single suspect.  In this case, we want to
 + * sort according to the suspect line numbers as opposed to the final
 + * image line numbers.  The function body is somewhat longish because
 + * it avoids unnecessary writes.
 + */
 +
 +static struct blame_entry *blame_merge(struct blame_entry *list1,
 +                                     struct blame_entry *list2)
 +{
 +      struct blame_entry *p1 = list1, *p2 = list2,
 +              **tail = &list1;
 +
 +      if (!p1)
 +              return p2;
 +      if (!p2)
 +              return p1;
 +
 +      if (p1->s_lno <= p2->s_lno) {
 +              do {
 +                      tail = &p1->next;
 +                      if ((p1 = *tail) == NULL) {
 +                              *tail = p2;
 +                              return list1;
 +                      }
 +              } while (p1->s_lno <= p2->s_lno);
 +      }
 +      for (;;) {
 +              *tail = p2;
 +              do {
 +                      tail = &p2->next;
 +                      if ((p2 = *tail) == NULL)  {
 +                              *tail = p1;
 +                              return list1;
 +                      }
 +              } while (p1->s_lno > p2->s_lno);
 +              *tail = p1;
 +              do {
 +                      tail = &p1->next;
 +                      if ((p1 = *tail) == NULL) {
 +                              *tail = p2;
 +                              return list1;
 +                      }
 +              } while (p1->s_lno <= p2->s_lno);
 +      }
 +}
 +
 +static void *get_next_blame(const void *p)
 +{
 +      return ((struct blame_entry *)p)->next;
 +}
 +
 +static void set_next_blame(void *p1, void *p2)
 +{
 +      ((struct blame_entry *)p1)->next = p2;
 +}
 +
 +/*
 + * Final image line numbers are all different, so we don't need a
 + * three-way comparison here.
 + */
 +
 +static int compare_blame_final(const void *p1, const void *p2)
 +{
 +      return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno
 +              ? 1 : -1;
 +}
 +
 +static int compare_blame_suspect(const void *p1, const void *p2)
 +{
 +      const struct blame_entry *s1 = p1, *s2 = p2;
 +      /*
 +       * to allow for collating suspects, we sort according to the
 +       * respective pointer value as the primary sorting criterion.
 +       * The actual relation is pretty unimportant as long as it
 +       * establishes a total order.  Comparing as integers gives us
 +       * that.
 +       */
 +      if (s1->suspect != s2->suspect)
 +              return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;
 +      if (s1->s_lno == s2->s_lno)
 +              return 0;
 +      return s1->s_lno > s2->s_lno ? 1 : -1;
 +}
 +
 +void blame_sort_final(struct blame_scoreboard *sb)
 +{
 +      sb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame,
 +                                compare_blame_final);
 +}
 +
 +static int compare_commits_by_reverse_commit_date(const void *a,
 +                                                const void *b,
 +                                                void *c)
 +{
 +      return -compare_commits_by_commit_date(a, b, c);
 +}
 +
 +/*
 + * For debugging -- origin is refcounted, and this asserts that
 + * we do not underflow.
 + */
 +static void sanity_check_refcnt(struct blame_scoreboard *sb)
 +{
 +      int baa = 0;
 +      struct blame_entry *ent;
 +
 +      for (ent = sb->ent; ent; ent = ent->next) {
 +              /* Nobody should have zero or negative refcnt */
 +              if (ent->suspect->refcnt <= 0) {
 +                      fprintf(stderr, "%s in %s has negative refcnt %d\n",
 +                              ent->suspect->path,
 +                              oid_to_hex(&ent->suspect->commit->object.oid),
 +                              ent->suspect->refcnt);
 +                      baa = 1;
 +              }
 +      }
 +      if (baa)
 +              sb->on_sanity_fail(sb, baa);
 +}
 +
 +/*
 + * If two blame entries that are next to each other came from
 + * contiguous lines in the same origin (i.e. <commit, path> pair),
 + * merge them together.
 + */
 +void blame_coalesce(struct blame_scoreboard *sb)
 +{
 +      struct blame_entry *ent, *next;
 +
 +      for (ent = sb->ent; ent && (next = ent->next); ent = next) {
 +              if (ent->suspect == next->suspect &&
 +                  ent->s_lno + ent->num_lines == next->s_lno) {
 +                      ent->num_lines += next->num_lines;
 +                      ent->next = next->next;
 +                      blame_origin_decref(next->suspect);
 +                      free(next);
 +                      ent->score = 0;
 +                      next = ent; /* again */
 +              }
 +      }
 +
 +      if (sb->debug) /* sanity */
 +              sanity_check_refcnt(sb);
 +}
 +
 +/*
 + * Merge the given sorted list of blames into a preexisting origin.
 + * If there were no previous blames to that commit, it is entered into
 + * the commit priority queue of the score board.
 + */
 +
 +static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin,
 +                       struct blame_entry *sorted)
 +{
 +      if (porigin->suspects)
 +              porigin->suspects = blame_merge(porigin->suspects, sorted);
 +      else {
 +              struct blame_origin *o;
 +              for (o = porigin->commit->util; o; o = o->next) {
 +                      if (o->suspects) {
 +                              porigin->suspects = sorted;
 +                              return;
 +                      }
 +              }
 +              porigin->suspects = sorted;
 +              prio_queue_put(&sb->commits, porigin->commit);
 +      }
 +}
 +
 +/*
 + * Fill the blob_sha1 field of an origin if it hasn't, so that later
 + * call to fill_origin_blob() can use it to locate the data.  blob_sha1
 + * for an origin is also used to pass the blame for the entire file to
 + * the parent to detect the case where a child's blob is identical to
 + * that of its parent's.
 + *
 + * This also fills origin->mode for corresponding tree path.
 + */
 +static int fill_blob_sha1_and_mode(struct blame_origin *origin)
 +{
 +      if (!is_null_oid(&origin->blob_oid))
 +              return 0;
 +      if (get_tree_entry(origin->commit->object.oid.hash,
 +                         origin->path,
 +                         origin->blob_oid.hash, &origin->mode))
 +              goto error_out;
 +      if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)
 +              goto error_out;
 +      return 0;
 + error_out:
 +      oidclr(&origin->blob_oid);
 +      origin->mode = S_IFINVALID;
 +      return -1;
 +}
 +
 +/*
 + * We have an origin -- check if the same path exists in the
 + * parent and return an origin structure to represent it.
 + */
 +static struct blame_origin *find_origin(struct commit *parent,
 +                                struct blame_origin *origin)
 +{
 +      struct blame_origin *porigin;
 +      struct diff_options diff_opts;
 +      const char *paths[2];
 +
 +      /* First check any existing origins */
 +      for (porigin = parent->util; porigin; porigin = porigin->next)
 +              if (!strcmp(porigin->path, origin->path)) {
 +                      /*
 +                       * The same path between origin and its parent
 +                       * without renaming -- the most common case.
 +                       */
 +                      return blame_origin_incref (porigin);
 +              }
 +
 +      /* See if the origin->path is different between parent
 +       * and origin first.  Most of the time they are the
 +       * same and diff-tree is fairly efficient about this.
 +       */
 +      diff_setup(&diff_opts);
 +      DIFF_OPT_SET(&diff_opts, RECURSIVE);
 +      diff_opts.detect_rename = 0;
 +      diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 +      paths[0] = origin->path;
 +      paths[1] = NULL;
 +
 +      parse_pathspec(&diff_opts.pathspec,
 +                     PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
 +                     PATHSPEC_LITERAL_PATH, "", paths);
 +      diff_setup_done(&diff_opts);
 +
 +      if (is_null_oid(&origin->commit->object.oid))
 +              do_diff_cache(&parent->tree->object.oid, &diff_opts);
 +      else
-               diff_tree_sha1(parent->tree->object.oid.hash,
-                              origin->commit->tree->object.oid.hash,
-                              "", &diff_opts);
++              diff_tree_oid(&parent->tree->object.oid,
++                            &origin->commit->tree->object.oid,
++                            "", &diff_opts);
 +      diffcore_std(&diff_opts);
 +
 +      if (!diff_queued_diff.nr) {
 +              /* The path is the same as parent */
 +              porigin = get_origin(parent, origin->path);
 +              oidcpy(&porigin->blob_oid, &origin->blob_oid);
 +              porigin->mode = origin->mode;
 +      } else {
 +              /*
 +               * Since origin->path is a pathspec, if the parent
 +               * commit had it as a directory, we will see a whole
 +               * bunch of deletion of files in the directory that we
 +               * do not care about.
 +               */
 +              int i;
 +              struct diff_filepair *p = NULL;
 +              for (i = 0; i < diff_queued_diff.nr; i++) {
 +                      const char *name;
 +                      p = diff_queued_diff.queue[i];
 +                      name = p->one->path ? p->one->path : p->two->path;
 +                      if (!strcmp(name, origin->path))
 +                              break;
 +              }
 +              if (!p)
 +                      die("internal error in blame::find_origin");
 +              switch (p->status) {
 +              default:
 +                      die("internal error in blame::find_origin (%c)",
 +                          p->status);
 +              case 'M':
 +                      porigin = get_origin(parent, origin->path);
 +                      oidcpy(&porigin->blob_oid, &p->one->oid);
 +                      porigin->mode = p->one->mode;
 +                      break;
 +              case 'A':
 +              case 'T':
 +                      /* Did not exist in parent, or type changed */
 +                      break;
 +              }
 +      }
 +      diff_flush(&diff_opts);
 +      clear_pathspec(&diff_opts.pathspec);
 +      return porigin;
 +}
 +
 +/*
 + * We have an origin -- find the path that corresponds to it in its
 + * parent and return an origin structure to represent it.
 + */
 +static struct blame_origin *find_rename(struct commit *parent,
 +                                struct blame_origin *origin)
 +{
 +      struct blame_origin *porigin = NULL;
 +      struct diff_options diff_opts;
 +      int i;
 +
 +      diff_setup(&diff_opts);
 +      DIFF_OPT_SET(&diff_opts, RECURSIVE);
 +      diff_opts.detect_rename = DIFF_DETECT_RENAME;
 +      diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 +      diff_opts.single_follow = origin->path;
 +      diff_setup_done(&diff_opts);
 +
 +      if (is_null_oid(&origin->commit->object.oid))
 +              do_diff_cache(&parent->tree->object.oid, &diff_opts);
 +      else
-        * force diff_tree_sha1() to feed all filepairs to diff_queue,
++              diff_tree_oid(&parent->tree->object.oid,
++                            &origin->commit->tree->object.oid,
++                            "", &diff_opts);
 +      diffcore_std(&diff_opts);
 +
 +      for (i = 0; i < diff_queued_diff.nr; i++) {
 +              struct diff_filepair *p = diff_queued_diff.queue[i];
 +              if ((p->status == 'R' || p->status == 'C') &&
 +                  !strcmp(p->two->path, origin->path)) {
 +                      porigin = get_origin(parent, p->one->path);
 +                      oidcpy(&porigin->blob_oid, &p->one->oid);
 +                      porigin->mode = p->one->mode;
 +                      break;
 +              }
 +      }
 +      diff_flush(&diff_opts);
 +      clear_pathspec(&diff_opts.pathspec);
 +      return porigin;
 +}
 +
 +/*
 + * Append a new blame entry to a given output queue.
 + */
 +static void add_blame_entry(struct blame_entry ***queue,
 +                          const struct blame_entry *src)
 +{
 +      struct blame_entry *e = xmalloc(sizeof(*e));
 +      memcpy(e, src, sizeof(*e));
 +      blame_origin_incref(e->suspect);
 +
 +      e->next = **queue;
 +      **queue = e;
 +      *queue = &e->next;
 +}
 +
 +/*
 + * src typically is on-stack; we want to copy the information in it to
 + * a malloced blame_entry that gets added to the given queue.  The
 + * origin of dst loses a refcnt.
 + */
 +static void dup_entry(struct blame_entry ***queue,
 +                    struct blame_entry *dst, struct blame_entry *src)
 +{
 +      blame_origin_incref(src->suspect);
 +      blame_origin_decref(dst->suspect);
 +      memcpy(dst, src, sizeof(*src));
 +      dst->next = **queue;
 +      **queue = dst;
 +      *queue = &dst->next;
 +}
 +
 +const char *blame_nth_line(struct blame_scoreboard *sb, long lno)
 +{
 +      return sb->final_buf + sb->lineno[lno];
 +}
 +
 +/*
 + * It is known that lines between tlno to same came from parent, and e
 + * has an overlap with that range.  it also is known that parent's
 + * line plno corresponds to e's line tlno.
 + *
 + *                <---- e ----->
 + *                   <------>
 + *                   <------------>
 + *             <------------>
 + *             <------------------>
 + *
 + * Split e into potentially three parts; before this chunk, the chunk
 + * to be blamed for the parent, and after that portion.
 + */
 +static void split_overlap(struct blame_entry *split,
 +                        struct blame_entry *e,
 +                        int tlno, int plno, int same,
 +                        struct blame_origin *parent)
 +{
 +      int chunk_end_lno;
 +      memset(split, 0, sizeof(struct blame_entry [3]));
 +
 +      if (e->s_lno < tlno) {
 +              /* there is a pre-chunk part not blamed on parent */
 +              split[0].suspect = blame_origin_incref(e->suspect);
 +              split[0].lno = e->lno;
 +              split[0].s_lno = e->s_lno;
 +              split[0].num_lines = tlno - e->s_lno;
 +              split[1].lno = e->lno + tlno - e->s_lno;
 +              split[1].s_lno = plno;
 +      }
 +      else {
 +              split[1].lno = e->lno;
 +              split[1].s_lno = plno + (e->s_lno - tlno);
 +      }
 +
 +      if (same < e->s_lno + e->num_lines) {
 +              /* there is a post-chunk part not blamed on parent */
 +              split[2].suspect = blame_origin_incref(e->suspect);
 +              split[2].lno = e->lno + (same - e->s_lno);
 +              split[2].s_lno = e->s_lno + (same - e->s_lno);
 +              split[2].num_lines = e->s_lno + e->num_lines - same;
 +              chunk_end_lno = split[2].lno;
 +      }
 +      else
 +              chunk_end_lno = e->lno + e->num_lines;
 +      split[1].num_lines = chunk_end_lno - split[1].lno;
 +
 +      /*
 +       * if it turns out there is nothing to blame the parent for,
 +       * forget about the splitting.  !split[1].suspect signals this.
 +       */
 +      if (split[1].num_lines < 1)
 +              return;
 +      split[1].suspect = blame_origin_incref(parent);
 +}
 +
 +/*
 + * split_overlap() divided an existing blame e into up to three parts
 + * in split.  Any assigned blame is moved to queue to
 + * reflect the split.
 + */
 +static void split_blame(struct blame_entry ***blamed,
 +                      struct blame_entry ***unblamed,
 +                      struct blame_entry *split,
 +                      struct blame_entry *e)
 +{
 +      if (split[0].suspect && split[2].suspect) {
 +              /* The first part (reuse storage for the existing entry e) */
 +              dup_entry(unblamed, e, &split[0]);
 +
 +              /* The last part -- me */
 +              add_blame_entry(unblamed, &split[2]);
 +
 +              /* ... and the middle part -- parent */
 +              add_blame_entry(blamed, &split[1]);
 +      }
 +      else if (!split[0].suspect && !split[2].suspect)
 +              /*
 +               * The parent covers the entire area; reuse storage for
 +               * e and replace it with the parent.
 +               */
 +              dup_entry(blamed, e, &split[1]);
 +      else if (split[0].suspect) {
 +              /* me and then parent */
 +              dup_entry(unblamed, e, &split[0]);
 +              add_blame_entry(blamed, &split[1]);
 +      }
 +      else {
 +              /* parent and then me */
 +              dup_entry(blamed, e, &split[1]);
 +              add_blame_entry(unblamed, &split[2]);
 +      }
 +}
 +
 +/*
 + * After splitting the blame, the origins used by the
 + * on-stack blame_entry should lose one refcnt each.
 + */
 +static void decref_split(struct blame_entry *split)
 +{
 +      int i;
 +
 +      for (i = 0; i < 3; i++)
 +              blame_origin_decref(split[i].suspect);
 +}
 +
 +/*
 + * reverse_blame reverses the list given in head, appending tail.
 + * That allows us to build lists in reverse order, then reverse them
 + * afterwards.  This can be faster than building the list in proper
 + * order right away.  The reason is that building in proper order
 + * requires writing a link in the _previous_ element, while building
 + * in reverse order just requires placing the list head into the
 + * _current_ element.
 + */
 +
 +static struct blame_entry *reverse_blame(struct blame_entry *head,
 +                                       struct blame_entry *tail)
 +{
 +      while (head) {
 +              struct blame_entry *next = head->next;
 +              head->next = tail;
 +              tail = head;
 +              head = next;
 +      }
 +      return tail;
 +}
 +
 +/*
 + * Process one hunk from the patch between the current suspect for
 + * blame_entry e and its parent.  This first blames any unfinished
 + * entries before the chunk (which is where target and parent start
 + * differing) on the parent, and then splits blame entries at the
 + * start and at the end of the difference region.  Since use of -M and
 + * -C options may lead to overlapping/duplicate source line number
 + * ranges, all we can rely on from sorting/merging is the order of the
 + * first suspect line number.
 + */
 +static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
 +                      int tlno, int offset, int same,
 +                      struct blame_origin *parent)
 +{
 +      struct blame_entry *e = **srcq;
 +      struct blame_entry *samep = NULL, *diffp = NULL;
 +
 +      while (e && e->s_lno < tlno) {
 +              struct blame_entry *next = e->next;
 +              /*
 +               * current record starts before differing portion.  If
 +               * it reaches into it, we need to split it up and
 +               * examine the second part separately.
 +               */
 +              if (e->s_lno + e->num_lines > tlno) {
 +                      /* Move second half to a new record */
 +                      int len = tlno - e->s_lno;
 +                      struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
 +                      n->suspect = e->suspect;
 +                      n->lno = e->lno + len;
 +                      n->s_lno = e->s_lno + len;
 +                      n->num_lines = e->num_lines - len;
 +                      e->num_lines = len;
 +                      e->score = 0;
 +                      /* Push new record to diffp */
 +                      n->next = diffp;
 +                      diffp = n;
 +              } else
 +                      blame_origin_decref(e->suspect);
 +              /* Pass blame for everything before the differing
 +               * chunk to the parent */
 +              e->suspect = blame_origin_incref(parent);
 +              e->s_lno += offset;
 +              e->next = samep;
 +              samep = e;
 +              e = next;
 +      }
 +      /*
 +       * As we don't know how much of a common stretch after this
 +       * diff will occur, the currently blamed parts are all that we
 +       * can assign to the parent for now.
 +       */
 +
 +      if (samep) {
 +              **dstq = reverse_blame(samep, **dstq);
 +              *dstq = &samep->next;
 +      }
 +      /*
 +       * Prepend the split off portions: everything after e starts
 +       * after the blameable portion.
 +       */
 +      e = reverse_blame(diffp, e);
 +
 +      /*
 +       * Now retain records on the target while parts are different
 +       * from the parent.
 +       */
 +      samep = NULL;
 +      diffp = NULL;
 +      while (e && e->s_lno < same) {
 +              struct blame_entry *next = e->next;
 +
 +              /*
 +               * If current record extends into sameness, need to split.
 +               */
 +              if (e->s_lno + e->num_lines > same) {
 +                      /*
 +                       * Move second half to a new record to be
 +                       * processed by later chunks
 +                       */
 +                      int len = same - e->s_lno;
 +                      struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
 +                      n->suspect = blame_origin_incref(e->suspect);
 +                      n->lno = e->lno + len;
 +                      n->s_lno = e->s_lno + len;
 +                      n->num_lines = e->num_lines - len;
 +                      e->num_lines = len;
 +                      e->score = 0;
 +                      /* Push new record to samep */
 +                      n->next = samep;
 +                      samep = n;
 +              }
 +              e->next = diffp;
 +              diffp = e;
 +              e = next;
 +      }
 +      **srcq = reverse_blame(diffp, reverse_blame(samep, e));
 +      /* Move across elements that are in the unblamable portion */
 +      if (diffp)
 +              *srcq = &diffp->next;
 +}
 +
 +struct blame_chunk_cb_data {
 +      struct blame_origin *parent;
 +      long offset;
 +      struct blame_entry **dstq;
 +      struct blame_entry **srcq;
 +};
 +
 +/* diff chunks are from parent to target */
 +static int blame_chunk_cb(long start_a, long count_a,
 +                        long start_b, long count_b, void *data)
 +{
 +      struct blame_chunk_cb_data *d = data;
 +      if (start_a - start_b != d->offset)
 +              die("internal error in blame::blame_chunk_cb");
 +      blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,
 +                  start_b + count_b, d->parent);
 +      d->offset = start_a + count_a - (start_b + count_b);
 +      return 0;
 +}
 +
 +/*
 + * We are looking at the origin 'target' and aiming to pass blame
 + * for the lines it is suspected to its parent.  Run diff to find
 + * which lines came from parent and pass blame for them.
 + */
 +static void pass_blame_to_parent(struct blame_scoreboard *sb,
 +                               struct blame_origin *target,
 +                               struct blame_origin *parent)
 +{
 +      mmfile_t file_p, file_o;
 +      struct blame_chunk_cb_data d;
 +      struct blame_entry *newdest = NULL;
 +
 +      if (!target->suspects)
 +              return; /* nothing remains for this target */
 +
 +      d.parent = parent;
 +      d.offset = 0;
 +      d.dstq = &newdest; d.srcq = &target->suspects;
 +
 +      fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);
 +      fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);
 +      sb->num_get_patch++;
 +
 +      if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))
 +              die("unable to generate diff (%s -> %s)",
 +                  oid_to_hex(&parent->commit->object.oid),
 +                  oid_to_hex(&target->commit->object.oid));
 +      /* The rest are the same as the parent */
 +      blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
 +      *d.dstq = NULL;
 +      queue_blames(sb, parent, newdest);
 +
 +      return;
 +}
 +
 +/*
 + * The lines in blame_entry after splitting blames many times can become
 + * very small and trivial, and at some point it becomes pointless to
 + * blame the parents.  E.g. "\t\t}\n\t}\n\n" appears everywhere in any
 + * ordinary C program, and it is not worth to say it was copied from
 + * totally unrelated file in the parent.
 + *
 + * Compute how trivial the lines in the blame_entry are.
 + */
 +unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e)
 +{
 +      unsigned score;
 +      const char *cp, *ep;
 +
 +      if (e->score)
 +              return e->score;
 +
 +      score = 1;
 +      cp = blame_nth_line(sb, e->lno);
 +      ep = blame_nth_line(sb, e->lno + e->num_lines);
 +      while (cp < ep) {
 +              unsigned ch = *((unsigned char *)cp);
 +              if (isalnum(ch))
 +                      score++;
 +              cp++;
 +      }
 +      e->score = score;
 +      return score;
 +}
 +
 +/*
 + * best_so_far[] and this[] are both a split of an existing blame_entry
 + * that passes blame to the parent.  Maintain best_so_far the best split
 + * so far, by comparing this and best_so_far and copying this into
 + * bst_so_far as needed.
 + */
 +static void copy_split_if_better(struct blame_scoreboard *sb,
 +                               struct blame_entry *best_so_far,
 +                               struct blame_entry *this)
 +{
 +      int i;
 +
 +      if (!this[1].suspect)
 +              return;
 +      if (best_so_far[1].suspect) {
 +              if (blame_entry_score(sb, &this[1]) < blame_entry_score(sb, &best_so_far[1]))
 +                      return;
 +      }
 +
 +      for (i = 0; i < 3; i++)
 +              blame_origin_incref(this[i].suspect);
 +      decref_split(best_so_far);
 +      memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
 +}
 +
 +/*
 + * We are looking at a part of the final image represented by
 + * ent (tlno and same are offset by ent->s_lno).
 + * tlno is where we are looking at in the final image.
 + * up to (but not including) same match preimage.
 + * plno is where we are looking at in the preimage.
 + *
 + * <-------------- final image ---------------------->
 + *       <------ent------>
 + *         ^tlno ^same
 + *    <---------preimage----->
 + *         ^plno
 + *
 + * All line numbers are 0-based.
 + */
 +static void handle_split(struct blame_scoreboard *sb,
 +                       struct blame_entry *ent,
 +                       int tlno, int plno, int same,
 +                       struct blame_origin *parent,
 +                       struct blame_entry *split)
 +{
 +      if (ent->num_lines <= tlno)
 +              return;
 +      if (tlno < same) {
 +              struct blame_entry this[3];
 +              tlno += ent->s_lno;
 +              same += ent->s_lno;
 +              split_overlap(this, ent, tlno, plno, same, parent);
 +              copy_split_if_better(sb, split, this);
 +              decref_split(this);
 +      }
 +}
 +
 +struct handle_split_cb_data {
 +      struct blame_scoreboard *sb;
 +      struct blame_entry *ent;
 +      struct blame_origin *parent;
 +      struct blame_entry *split;
 +      long plno;
 +      long tlno;
 +};
 +
 +static int handle_split_cb(long start_a, long count_a,
 +                         long start_b, long count_b, void *data)
 +{
 +      struct handle_split_cb_data *d = data;
 +      handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
 +                   d->split);
 +      d->plno = start_a + count_a;
 +      d->tlno = start_b + count_b;
 +      return 0;
 +}
 +
 +/*
 + * Find the lines from parent that are the same as ent so that
 + * we can pass blames to it.  file_p has the blob contents for
 + * the parent.
 + */
 +static void find_copy_in_blob(struct blame_scoreboard *sb,
 +                            struct blame_entry *ent,
 +                            struct blame_origin *parent,
 +                            struct blame_entry *split,
 +                            mmfile_t *file_p)
 +{
 +      const char *cp;
 +      mmfile_t file_o;
 +      struct handle_split_cb_data d;
 +
 +      memset(&d, 0, sizeof(d));
 +      d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
 +      /*
 +       * Prepare mmfile that contains only the lines in ent.
 +       */
 +      cp = blame_nth_line(sb, ent->lno);
 +      file_o.ptr = (char *) cp;
 +      file_o.size = blame_nth_line(sb, ent->lno + ent->num_lines) - cp;
 +
 +      /*
 +       * file_o is a part of final image we are annotating.
 +       * file_p partially may match that image.
 +       */
 +      memset(split, 0, sizeof(struct blame_entry [3]));
 +      if (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))
 +              die("unable to generate diff (%s)",
 +                  oid_to_hex(&parent->commit->object.oid));
 +      /* remainder, if any, all match the preimage */
 +      handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
 +}
 +
 +/* Move all blame entries from list *source that have a score smaller
 + * than score_min to the front of list *small.
 + * Returns a pointer to the link pointing to the old head of the small list.
 + */
 +
 +static struct blame_entry **filter_small(struct blame_scoreboard *sb,
 +                                       struct blame_entry **small,
 +                                       struct blame_entry **source,
 +                                       unsigned score_min)
 +{
 +      struct blame_entry *p = *source;
 +      struct blame_entry *oldsmall = *small;
 +      while (p) {
 +              if (blame_entry_score(sb, p) <= score_min) {
 +                      *small = p;
 +                      small = &p->next;
 +                      p = *small;
 +              } else {
 +                      *source = p;
 +                      source = &p->next;
 +                      p = *source;
 +              }
 +      }
 +      *small = oldsmall;
 +      *source = NULL;
 +      return small;
 +}
 +
 +/*
 + * See if lines currently target is suspected for can be attributed to
 + * parent.
 + */
 +static void find_move_in_parent(struct blame_scoreboard *sb,
 +                              struct blame_entry ***blamed,
 +                              struct blame_entry **toosmall,
 +                              struct blame_origin *target,
 +                              struct blame_origin *parent)
 +{
 +      struct blame_entry *e, split[3];
 +      struct blame_entry *unblamed = target->suspects;
 +      struct blame_entry *leftover = NULL;
 +      mmfile_t file_p;
 +
 +      if (!unblamed)
 +              return; /* nothing remains for this target */
 +
 +      fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);
 +      if (!file_p.ptr)
 +              return;
 +
 +      /* At each iteration, unblamed has a NULL-terminated list of
 +       * entries that have not yet been tested for blame.  leftover
 +       * contains the reversed list of entries that have been tested
 +       * without being assignable to the parent.
 +       */
 +      do {
 +              struct blame_entry **unblamedtail = &unblamed;
 +              struct blame_entry *next;
 +              for (e = unblamed; e; e = next) {
 +                      next = e->next;
 +                      find_copy_in_blob(sb, e, parent, split, &file_p);
 +                      if (split[1].suspect &&
 +                          sb->move_score < blame_entry_score(sb, &split[1])) {
 +                              split_blame(blamed, &unblamedtail, split, e);
 +                      } else {
 +                              e->next = leftover;
 +                              leftover = e;
 +                      }
 +                      decref_split(split);
 +              }
 +              *unblamedtail = NULL;
 +              toosmall = filter_small(sb, toosmall, &unblamed, sb->move_score);
 +      } while (unblamed);
 +      target->suspects = reverse_blame(leftover, NULL);
 +}
 +
 +struct blame_list {
 +      struct blame_entry *ent;
 +      struct blame_entry split[3];
 +};
 +
 +/*
 + * Count the number of entries the target is suspected for,
 + * and prepare a list of entry and the best split.
 + */
 +static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
 +                                         int *num_ents_p)
 +{
 +      struct blame_entry *e;
 +      int num_ents, i;
 +      struct blame_list *blame_list = NULL;
 +
 +      for (e = unblamed, num_ents = 0; e; e = e->next)
 +              num_ents++;
 +      if (num_ents) {
 +              blame_list = xcalloc(num_ents, sizeof(struct blame_list));
 +              for (e = unblamed, i = 0; e; e = e->next)
 +                      blame_list[i++].ent = e;
 +      }
 +      *num_ents_p = num_ents;
 +      return blame_list;
 +}
 +
 +/*
 + * For lines target is suspected for, see if we can find code movement
 + * across file boundary from the parent commit.  porigin is the path
 + * in the parent we already tried.
 + */
 +static void find_copy_in_parent(struct blame_scoreboard *sb,
 +                              struct blame_entry ***blamed,
 +                              struct blame_entry **toosmall,
 +                              struct blame_origin *target,
 +                              struct commit *parent,
 +                              struct blame_origin *porigin,
 +                              int opt)
 +{
 +      struct diff_options diff_opts;
 +      int i, j;
 +      struct blame_list *blame_list;
 +      int num_ents;
 +      struct blame_entry *unblamed = target->suspects;
 +      struct blame_entry *leftover = NULL;
 +
 +      if (!unblamed)
 +              return; /* nothing remains for this target */
 +
 +      diff_setup(&diff_opts);
 +      DIFF_OPT_SET(&diff_opts, RECURSIVE);
 +      diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
 +
 +      diff_setup_done(&diff_opts);
 +
 +      /* Try "find copies harder" on new path if requested;
 +       * we do not want to use diffcore_rename() actually to
 +       * match things up; find_copies_harder is set only to
-               diff_tree_sha1(parent->tree->object.oid.hash,
-                              target->commit->tree->object.oid.hash,
-                              "", &diff_opts);
++       * force diff_tree_oid() to feed all filepairs to diff_queue,
 +       * and this code needs to be after diff_setup_done(), which
 +       * usually makes find-copies-harder imply copy detection.
 +       */
 +      if ((opt & PICKAXE_BLAME_COPY_HARDEST)
 +          || ((opt & PICKAXE_BLAME_COPY_HARDER)
 +              && (!porigin || strcmp(target->path, porigin->path))))
 +              DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
 +
 +      if (is_null_oid(&target->commit->object.oid))
 +              do_diff_cache(&parent->tree->object.oid, &diff_opts);
 +      else
++              diff_tree_oid(&parent->tree->object.oid,
++                            &target->commit->tree->object.oid,
++                            "", &diff_opts);
 +
 +      if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
 +              diffcore_std(&diff_opts);
 +
 +      do {
 +              struct blame_entry **unblamedtail = &unblamed;
 +              blame_list = setup_blame_list(unblamed, &num_ents);
 +
 +              for (i = 0; i < diff_queued_diff.nr; i++) {
 +                      struct diff_filepair *p = diff_queued_diff.queue[i];
 +                      struct blame_origin *norigin;
 +                      mmfile_t file_p;
 +                      struct blame_entry this[3];
 +
 +                      if (!DIFF_FILE_VALID(p->one))
 +                              continue; /* does not exist in parent */
 +                      if (S_ISGITLINK(p->one->mode))
 +                              continue; /* ignore git links */
 +                      if (porigin && !strcmp(p->one->path, porigin->path))
 +                              /* find_move already dealt with this path */
 +                              continue;
 +
 +                      norigin = get_origin(parent, p->one->path);
 +                      oidcpy(&norigin->blob_oid, &p->one->oid);
 +                      norigin->mode = p->one->mode;
 +                      fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);
 +                      if (!file_p.ptr)
 +                              continue;
 +
 +                      for (j = 0; j < num_ents; j++) {
 +                              find_copy_in_blob(sb, blame_list[j].ent,
 +                                                norigin, this, &file_p);
 +                              copy_split_if_better(sb, blame_list[j].split,
 +                                                   this);
 +                              decref_split(this);
 +                      }
 +                      blame_origin_decref(norigin);
 +              }
 +
 +              for (j = 0; j < num_ents; j++) {
 +                      struct blame_entry *split = blame_list[j].split;
 +                      if (split[1].suspect &&
 +                          sb->copy_score < blame_entry_score(sb, &split[1])) {
 +                              split_blame(blamed, &unblamedtail, split,
 +                                          blame_list[j].ent);
 +                      } else {
 +                              blame_list[j].ent->next = leftover;
 +                              leftover = blame_list[j].ent;
 +                      }
 +                      decref_split(split);
 +              }
 +              free(blame_list);
 +              *unblamedtail = NULL;
 +              toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score);
 +      } while (unblamed);
 +      target->suspects = reverse_blame(leftover, NULL);
 +      diff_flush(&diff_opts);
 +      clear_pathspec(&diff_opts.pathspec);
 +}
 +
 +/*
 + * The blobs of origin and porigin exactly match, so everything
 + * origin is suspected for can be blamed on the parent.
 + */
 +static void pass_whole_blame(struct blame_scoreboard *sb,
 +                           struct blame_origin *origin, struct blame_origin *porigin)
 +{
 +      struct blame_entry *e, *suspects;
 +
 +      if (!porigin->file.ptr && origin->file.ptr) {
 +              /* Steal its file */
 +              porigin->file = origin->file;
 +              origin->file.ptr = NULL;
 +      }
 +      suspects = origin->suspects;
 +      origin->suspects = NULL;
 +      for (e = suspects; e; e = e->next) {
 +              blame_origin_incref(porigin);
 +              blame_origin_decref(e->suspect);
 +              e->suspect = porigin;
 +      }
 +      queue_blames(sb, porigin, suspects);
 +}
 +
 +/*
 + * We pass blame from the current commit to its parents.  We keep saying
 + * "parent" (and "porigin"), but what we mean is to find scapegoat to
 + * exonerate ourselves.
 + */
 +static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit,
 +                                      int reverse)
 +{
 +      if (!reverse) {
 +              if (revs->first_parent_only &&
 +                  commit->parents &&
 +                  commit->parents->next) {
 +                      free_commit_list(commit->parents->next);
 +                      commit->parents->next = NULL;
 +              }
 +              return commit->parents;
 +      }
 +      return lookup_decoration(&revs->children, &commit->object);
 +}
 +
 +static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)
 +{
 +      struct commit_list *l = first_scapegoat(revs, commit, reverse);
 +      return commit_list_count(l);
 +}
 +
 +/* Distribute collected unsorted blames to the respected sorted lists
 + * in the various origins.
 + */
 +static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed)
 +{
 +      blamed = llist_mergesort(blamed, get_next_blame, set_next_blame,
 +                               compare_blame_suspect);
 +      while (blamed)
 +      {
 +              struct blame_origin *porigin = blamed->suspect;
 +              struct blame_entry *suspects = NULL;
 +              do {
 +                      struct blame_entry *next = blamed->next;
 +                      blamed->next = suspects;
 +                      suspects = blamed;
 +                      blamed = next;
 +              } while (blamed && blamed->suspect == porigin);
 +              suspects = reverse_blame(suspects, NULL);
 +              queue_blames(sb, porigin, suspects);
 +      }
 +}
 +
 +#define MAXSG 16
 +
 +static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt)
 +{
 +      struct rev_info *revs = sb->revs;
 +      int i, pass, num_sg;
 +      struct commit *commit = origin->commit;
 +      struct commit_list *sg;
 +      struct blame_origin *sg_buf[MAXSG];
 +      struct blame_origin *porigin, **sg_origin = sg_buf;
 +      struct blame_entry *toosmall = NULL;
 +      struct blame_entry *blames, **blametail = &blames;
 +
 +      num_sg = num_scapegoats(revs, commit, sb->reverse);
 +      if (!num_sg)
 +              goto finish;
 +      else if (num_sg < ARRAY_SIZE(sg_buf))
 +              memset(sg_buf, 0, sizeof(sg_buf));
 +      else
 +              sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
 +
 +      /*
 +       * The first pass looks for unrenamed path to optimize for
 +       * common cases, then we look for renames in the second pass.
 +       */
 +      for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) {
 +              struct blame_origin *(*find)(struct commit *, struct blame_origin *);
 +              find = pass ? find_rename : find_origin;
 +
 +              for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
 +                   i < num_sg && sg;
 +                   sg = sg->next, i++) {
 +                      struct commit *p = sg->item;
 +                      int j, same;
 +
 +                      if (sg_origin[i])
 +                              continue;
 +                      if (parse_commit(p))
 +                              continue;
 +                      porigin = find(p, origin);
 +                      if (!porigin)
 +                              continue;
 +                      if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
 +                              pass_whole_blame(sb, origin, porigin);
 +                              blame_origin_decref(porigin);
 +                              goto finish;
 +                      }
 +                      for (j = same = 0; j < i; j++)
 +                              if (sg_origin[j] &&
 +                                  !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {
 +                                      same = 1;
 +                                      break;
 +                              }
 +                      if (!same)
 +                              sg_origin[i] = porigin;
 +                      else
 +                              blame_origin_decref(porigin);
 +              }
 +      }
 +
 +      sb->num_commits++;
 +      for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
 +           i < num_sg && sg;
 +           sg = sg->next, i++) {
 +              struct blame_origin *porigin = sg_origin[i];
 +              if (!porigin)
 +                      continue;
 +              if (!origin->previous) {
 +                      blame_origin_incref(porigin);
 +                      origin->previous = porigin;
 +              }
 +              pass_blame_to_parent(sb, origin, porigin);
 +              if (!origin->suspects)
 +                      goto finish;
 +      }
 +
 +      /*
 +       * Optionally find moves in parents' files.
 +       */
 +      if (opt & PICKAXE_BLAME_MOVE) {
 +              filter_small(sb, &toosmall, &origin->suspects, sb->move_score);
 +              if (origin->suspects) {
 +                      for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
 +                           i < num_sg && sg;
 +                           sg = sg->next, i++) {
 +                              struct blame_origin *porigin = sg_origin[i];
 +                              if (!porigin)
 +                                      continue;
 +                              find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
 +                              if (!origin->suspects)
 +                                      break;
 +                      }
 +              }
 +      }
 +
 +      /*
 +       * Optionally find copies from parents' files.
 +       */
 +      if (opt & PICKAXE_BLAME_COPY) {
 +              if (sb->copy_score > sb->move_score)
 +                      filter_small(sb, &toosmall, &origin->suspects, sb->copy_score);
 +              else if (sb->copy_score < sb->move_score) {
 +                      origin->suspects = blame_merge(origin->suspects, toosmall);
 +                      toosmall = NULL;
 +                      filter_small(sb, &toosmall, &origin->suspects, sb->copy_score);
 +              }
 +              if (!origin->suspects)
 +                      goto finish;
 +
 +              for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
 +                   i < num_sg && sg;
 +                   sg = sg->next, i++) {
 +                      struct blame_origin *porigin = sg_origin[i];
 +                      find_copy_in_parent(sb, &blametail, &toosmall,
 +                                          origin, sg->item, porigin, opt);
 +                      if (!origin->suspects)
 +                              goto finish;
 +              }
 +      }
 +
 +finish:
 +      *blametail = NULL;
 +      distribute_blame(sb, blames);
 +      /*
 +       * prepend toosmall to origin->suspects
 +       *
 +       * There is no point in sorting: this ends up on a big
 +       * unsorted list in the caller anyway.
 +       */
 +      if (toosmall) {
 +              struct blame_entry **tail = &toosmall;
 +              while (*tail)
 +                      tail = &(*tail)->next;
 +              *tail = origin->suspects;
 +              origin->suspects = toosmall;
 +      }
 +      for (i = 0; i < num_sg; i++) {
 +              if (sg_origin[i]) {
 +                      drop_origin_blob(sg_origin[i]);
 +                      blame_origin_decref(sg_origin[i]);
 +              }
 +      }
 +      drop_origin_blob(origin);
 +      if (sg_buf != sg_origin)
 +              free(sg_origin);
 +}
 +
 +/*
 + * The main loop -- while we have blobs with lines whose true origin
 + * is still unknown, pick one blob, and allow its lines to pass blames
 + * to its parents. */
 +void assign_blame(struct blame_scoreboard *sb, int opt)
 +{
 +      struct rev_info *revs = sb->revs;
 +      struct commit *commit = prio_queue_get(&sb->commits);
 +
 +      while (commit) {
 +              struct blame_entry *ent;
 +              struct blame_origin *suspect = commit->util;
 +
 +              /* find one suspect to break down */
 +              while (suspect && !suspect->suspects)
 +                      suspect = suspect->next;
 +
 +              if (!suspect) {
 +                      commit = prio_queue_get(&sb->commits);
 +                      continue;
 +              }
 +
 +              assert(commit == suspect->commit);
 +
 +              /*
 +               * We will use this suspect later in the loop,
 +               * so hold onto it in the meantime.
 +               */
 +              blame_origin_incref(suspect);
 +              parse_commit(commit);
 +              if (sb->reverse ||
 +                  (!(commit->object.flags & UNINTERESTING) &&
 +                   !(revs->max_age != -1 && commit->date < revs->max_age)))
 +                      pass_blame(sb, suspect, opt);
 +              else {
 +                      commit->object.flags |= UNINTERESTING;
 +                      if (commit->object.parsed)
 +                              mark_parents_uninteresting(commit);
 +              }
 +              /* treat root commit as boundary */
 +              if (!commit->parents && !sb->show_root)
 +                      commit->object.flags |= UNINTERESTING;
 +
 +              /* Take responsibility for the remaining entries */
 +              ent = suspect->suspects;
 +              if (ent) {
 +                      suspect->guilty = 1;
 +                      for (;;) {
 +                              struct blame_entry *next = ent->next;
 +                              if (sb->found_guilty_entry)
 +                                      sb->found_guilty_entry(ent, sb->found_guilty_entry_data);
 +                              if (next) {
 +                                      ent = next;
 +                                      continue;
 +                              }
 +                              ent->next = sb->ent;
 +                              sb->ent = suspect->suspects;
 +                              suspect->suspects = NULL;
 +                              break;
 +                      }
 +              }
 +              blame_origin_decref(suspect);
 +
 +              if (sb->debug) /* sanity */
 +                      sanity_check_refcnt(sb);
 +      }
 +}
 +
 +static const char *get_next_line(const char *start, const char *end)
 +{
 +      const char *nl = memchr(start, '\n', end - start);
 +      return nl ? nl + 1 : end;
 +}
 +
 +/*
 + * To allow quick access to the contents of nth line in the
 + * final image, prepare an index in the scoreboard.
 + */
 +static int prepare_lines(struct blame_scoreboard *sb)
 +{
 +      const char *buf = sb->final_buf;
 +      unsigned long len = sb->final_buf_size;
 +      const char *end = buf + len;
 +      const char *p;
 +      int *lineno;
 +      int num = 0;
 +
 +      for (p = buf; p < end; p = get_next_line(p, end))
 +              num++;
 +
 +      ALLOC_ARRAY(sb->lineno, num + 1);
 +      lineno = sb->lineno;
 +
 +      for (p = buf; p < end; p = get_next_line(p, end))
 +              *lineno++ = p - buf;
 +
 +      *lineno = len;
 +
 +      sb->num_lines = num;
 +      return sb->num_lines;
 +}
 +
 +static struct commit *find_single_final(struct rev_info *revs,
 +                                      const char **name_p)
 +{
 +      int i;
 +      struct commit *found = NULL;
 +      const char *name = NULL;
 +
 +      for (i = 0; i < revs->pending.nr; i++) {
 +              struct object *obj = revs->pending.objects[i].item;
 +              if (obj->flags & UNINTERESTING)
 +                      continue;
 +              obj = deref_tag(obj, NULL, 0);
 +              if (obj->type != OBJ_COMMIT)
 +                      die("Non commit %s?", revs->pending.objects[i].name);
 +              if (found)
 +                      die("More than one commit to dig from %s and %s?",
 +                          revs->pending.objects[i].name, name);
 +              found = (struct commit *)obj;
 +              name = revs->pending.objects[i].name;
 +      }
 +      if (name_p)
 +              *name_p = name;
 +      return found;
 +}
 +
 +static struct commit *dwim_reverse_initial(struct rev_info *revs,
 +                                         const char **name_p)
 +{
 +      /*
 +       * DWIM "git blame --reverse ONE -- PATH" as
 +       * "git blame --reverse ONE..HEAD -- PATH" but only do so
 +       * when it makes sense.
 +       */
 +      struct object *obj;
 +      struct commit *head_commit;
 +      struct object_id head_oid;
 +
 +      if (revs->pending.nr != 1)
 +              return NULL;
 +
 +      /* Is that sole rev a committish? */
 +      obj = revs->pending.objects[0].item;
 +      obj = deref_tag(obj, NULL, 0);
 +      if (obj->type != OBJ_COMMIT)
 +              return NULL;
 +
 +      /* Do we have HEAD? */
 +      if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
 +              return NULL;
 +      head_commit = lookup_commit_reference_gently(&head_oid, 1);
 +      if (!head_commit)
 +              return NULL;
 +
 +      /* Turn "ONE" into "ONE..HEAD" then */
 +      obj->flags |= UNINTERESTING;
 +      add_pending_object(revs, &head_commit->object, "HEAD");
 +
 +      if (name_p)
 +              *name_p = revs->pending.objects[0].name;
 +      return (struct commit *)obj;
 +}
 +
 +static struct commit *find_single_initial(struct rev_info *revs,
 +                                        const char **name_p)
 +{
 +      int i;
 +      struct commit *found = NULL;
 +      const char *name = NULL;
 +
 +      /*
 +       * There must be one and only one negative commit, and it must be
 +       * the boundary.
 +       */
 +      for (i = 0; i < revs->pending.nr; i++) {
 +              struct object *obj = revs->pending.objects[i].item;
 +              if (!(obj->flags & UNINTERESTING))
 +                      continue;
 +              obj = deref_tag(obj, NULL, 0);
 +              if (obj->type != OBJ_COMMIT)
 +                      die("Non commit %s?", revs->pending.objects[i].name);
 +              if (found)
 +                      die("More than one commit to dig up from, %s and %s?",
 +                          revs->pending.objects[i].name, name);
 +              found = (struct commit *) obj;
 +              name = revs->pending.objects[i].name;
 +      }
 +
 +      if (!name)
 +              found = dwim_reverse_initial(revs, &name);
 +      if (!name)
 +              die("No commit to dig up from?");
 +
 +      if (name_p)
 +              *name_p = name;
 +      return found;
 +}
 +
 +void init_scoreboard(struct blame_scoreboard *sb)
 +{
 +      memset(sb, 0, sizeof(struct blame_scoreboard));
 +      sb->move_score = BLAME_DEFAULT_MOVE_SCORE;
 +      sb->copy_score = BLAME_DEFAULT_COPY_SCORE;
 +}
 +
 +void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig)
 +{
 +      const char *final_commit_name = NULL;
 +      struct blame_origin *o;
 +      struct commit *final_commit = NULL;
 +      enum object_type type;
 +
 +      if (sb->reverse && sb->contents_from)
 +              die(_("--contents and --reverse do not blend well."));
 +
 +      if (!sb->reverse) {
 +              sb->final = find_single_final(sb->revs, &final_commit_name);
 +              sb->commits.compare = compare_commits_by_commit_date;
 +      } else {
 +              sb->final = find_single_initial(sb->revs, &final_commit_name);
 +              sb->commits.compare = compare_commits_by_reverse_commit_date;
 +      }
 +
 +      if (sb->final && sb->contents_from)
 +              die(_("cannot use --contents with final commit object name"));
 +
 +      if (sb->reverse && sb->revs->first_parent_only)
 +              sb->revs->children.name = NULL;
 +
 +      if (!sb->final) {
 +              /*
 +               * "--not A B -- path" without anything positive;
 +               * do not default to HEAD, but use the working tree
 +               * or "--contents".
 +               */
 +              setup_work_tree();
 +              sb->final = fake_working_tree_commit(&sb->revs->diffopt,
 +                                                   path, sb->contents_from);
 +              add_pending_object(sb->revs, &(sb->final->object), ":");
 +      }
 +
 +      if (sb->reverse && sb->revs->first_parent_only) {
 +              final_commit = find_single_final(sb->revs, NULL);
 +              if (!final_commit)
 +                      die(_("--reverse and --first-parent together require specified latest commit"));
 +      }
 +
 +      /*
 +       * If we have bottom, this will mark the ancestors of the
 +       * bottom commits we would reach while traversing as
 +       * uninteresting.
 +       */
 +      if (prepare_revision_walk(sb->revs))
 +              die(_("revision walk setup failed"));
 +
 +      if (sb->reverse && sb->revs->first_parent_only) {
 +              struct commit *c = final_commit;
 +
 +              sb->revs->children.name = "children";
 +              while (c->parents &&
 +                     oidcmp(&c->object.oid, &sb->final->object.oid)) {
 +                      struct commit_list *l = xcalloc(1, sizeof(*l));
 +
 +                      l->item = c;
 +                      if (add_decoration(&sb->revs->children,
 +                                         &c->parents->item->object, l))
 +                              die("BUG: not unique item in first-parent chain");
 +                      c = c->parents->item;
 +              }
 +
 +              if (oidcmp(&c->object.oid, &sb->final->object.oid))
 +                      die(_("--reverse --first-parent together require range along first-parent chain"));
 +      }
 +
 +      if (is_null_oid(&sb->final->object.oid)) {
 +              o = sb->final->util;
 +              sb->final_buf = xmemdupz(o->file.ptr, o->file.size);
 +              sb->final_buf_size = o->file.size;
 +      }
 +      else {
 +              o = get_origin(sb->final, path);
 +              if (fill_blob_sha1_and_mode(o))
 +                      die(_("no such path %s in %s"), path, final_commit_name);
 +
 +              if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&
 +                  textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
 +                                  &sb->final_buf_size))
 +                      ;
 +              else
 +                      sb->final_buf = read_sha1_file(o->blob_oid.hash, &type,
 +                                                     &sb->final_buf_size);
 +
 +              if (!sb->final_buf)
 +                      die(_("cannot read blob %s for path %s"),
 +                          oid_to_hex(&o->blob_oid),
 +                          path);
 +      }
 +      sb->num_read_blob++;
 +      prepare_lines(sb);
 +
 +      if (orig)
 +              *orig = o;
 +}
 +
 +
 +
 +struct blame_entry *blame_entry_prepend(struct blame_entry *head,
 +                                      long start, long end,
 +                                      struct blame_origin *o)
 +{
 +      struct blame_entry *new_head = xcalloc(1, sizeof(struct blame_entry));
 +      new_head->lno = start;
 +      new_head->num_lines = end - start;
 +      new_head->suspect = o;
 +      new_head->s_lno = start;
 +      new_head->next = head;
 +      blame_origin_incref(o);
 +      return new_head;
 +}
diff --combined builtin/am.c
index 8881d736151bf266cc8544a717cba960c078eff5,d9fdddac4affa80ac970e8084331f2ab1289f375..3985f9a89f985baa139ea69f5fde8ce1384871f6
@@@ -563,7 -563,7 +563,7 @@@ static int copy_notes_for_rebase(const 
                        goto finish;
                }
  
-               if (copy_note_for_rewrite(c, from_obj.hash, to_obj.hash))
+               if (copy_note_for_rewrite(c, &from_obj, &to_obj))
                        ret = error(_("Failed to copy notes from '%s' to '%s'"),
                                        oid_to_hex(&from_obj), oid_to_hex(&to_obj));
        }
@@@ -1275,8 -1275,12 +1275,8 @@@ static int parse_mail(struct am_state *
                die("BUG: invalid value for state->scissors");
        }
  
 -      mi.input = fopen(mail, "r");
 -      if (!mi.input)
 -              die("could not open input");
 -      mi.output = fopen(am_path(state, "info"), "w");
 -      if (!mi.output)
 -              die("could not open output 'info'");
 +      mi.input = xfopen(mail, "r");
 +      mi.output = xfopen(am_path(state, "info"), "w");
        if (mailinfo(&mi, am_path(state, "msg"), am_path(state, "patch")))
                die("could not parse patch");
  
@@@ -2307,9 -2311,6 +2307,9 @@@ int cmd_am(int argc, const char **argv
                OPT_END()
        };
  
 +      if (argc == 2 && !strcmp(argv[1], "-h"))
 +              usage_with_options(usage, options);
 +
        git_config(git_am_config, NULL);
  
        am_state_init(&state);
diff --combined builtin/commit.c
index ef52457effc1f519a53c105efcb24d5e19048f6c,758781004533b4bda9e8475c065e57f75a22d571..e3c9e190b06a63250a9c7eff3e256efcb10660f7
@@@ -1699,7 -1699,10 +1699,7 @@@ int cmd_commit(int argc, const char **a
                if (!reflog_msg)
                        reflog_msg = "commit (merge)";
                pptr = commit_list_append(current_head, pptr);
 -              fp = fopen(git_path_merge_head(), "r");
 -              if (fp == NULL)
 -                      die_errno(_("could not open '%s' for reading"),
 -                                git_path_merge_head());
 +              fp = xfopen(git_path_merge_head(), "r");
                while (strbuf_getline_lf(&m, fp) != EOF) {
                        struct commit *parent;
  
                cfg = init_copy_notes_for_rewrite("amend");
                if (cfg) {
                        /* we are amending, so current_head is not NULL */
-                       copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash);
+                       copy_note_for_rewrite(cfg, &current_head->object.oid, &oid);
                        finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
                }
                run_rewrite_hook(&current_head->object.oid, &oid);
diff --combined builtin/diff-tree.c
index 492245f4280cdda6a8d9baa85ed943f5b245bf62,8b26a66a959d84aa7d337323ad99ec159e9e471e..1fd06eac4b9b56dc2f3e21ef3045ff66507cdaf3
@@@ -7,7 -7,7 +7,7 @@@
  
  static struct rev_info log_tree_opt;
  
- static int diff_tree_commit_sha1(const struct object_id *oid)
+ static int diff_tree_commit_oid(const struct object_id *oid)
  {
        struct commit *commit = lookup_commit_reference(oid);
        if (!commit)
@@@ -49,8 -49,8 +49,8 @@@ static int stdin_diff_trees(struct tre
                return -1;
        printf("%s %s\n", oid_to_hex(&tree1->object.oid),
                          oid_to_hex(&tree2->object.oid));
-       diff_tree_sha1(tree1->object.oid.hash, tree2->object.oid.hash,
-                      "", &log_tree_opt.diffopt);
+       diff_tree_oid(&tree1->object.oid, &tree2->object.oid,
+                     "", &log_tree_opt.diffopt);
        log_tree_diff_flush(&log_tree_opt);
        return 0;
  }
@@@ -98,16 -98,15 +98,15 @@@ static void diff_tree_tweak_rev(struct 
  
  int cmd_diff_tree(int argc, const char **argv, const char *prefix)
  {
-       int nr_sha1;
        char line[1000];
        struct object *tree1, *tree2;
        static struct rev_info *opt = &log_tree_opt;
        struct setup_revision_opt s_r_opt;
        int read_stdin = 0;
  
 +      git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        init_revisions(opt, prefix);
        gitmodules_config();
 -      git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        opt->abbrev = 0;
        opt->diff = 1;
        opt->disable_stdin = 1;
        }
  
        /*
 -       * NOTE! We expect "a ^b" to be equal to "a..b", so we
 -       * reverse the order of the objects if the second one
 -       * is marked UNINTERESTING.
 +       * NOTE!  We expect "a..b" to expand to "^a b" but it is
 +       * perfectly valid for revision range parser to yield "b ^a",
 +       * which means the same thing. If we get the latter, i.e. the
 +       * second one is marked UNINTERESTING, we recover the original
 +       * order the user gave, i.e. "a..b", by swapping the trees.
         */
-       nr_sha1 = opt->pending.nr;
-       switch (nr_sha1) {
+       switch (opt->pending.nr) {
        case 0:
                if (!read_stdin)
                        usage(diff_tree_usage);
                break;
        case 1:
                tree1 = opt->pending.objects[0].item;
-               diff_tree_commit_sha1(&tree1->oid);
+               diff_tree_commit_oid(&tree1->oid);
                break;
        case 2:
                tree1 = opt->pending.objects[0].item;
                if (tree2->flags & UNINTERESTING) {
                        SWAP(tree2, tree1);
                }
-               diff_tree_sha1(tree1->oid.hash,
-                              tree2->oid.hash,
-                              "", &opt->diffopt);
+               diff_tree_oid(&tree1->oid, &tree2->oid, "", &opt->diffopt);
                log_tree_diff_flush(opt);
                break;
        }
diff --combined builtin/diff.c
index 0c8f86e40da6537d57f5ed6ca0ecdd005e29c8f5,4c6a1a962fa3f86cf7c17870eadf2f97f46d875a..d9152c21bf5a8aa41778f838b4d804ac698bdc2b
  #define DIFF_NO_INDEX_EXPLICIT 1
  #define DIFF_NO_INDEX_IMPLICIT 2
  
 -struct blobinfo {
 -      struct object_id oid;
 -      const char *name;
 -      unsigned mode;
 -};
 -
  static const char builtin_diff_usage[] =
  "git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
  
 +static const char *blob_path(struct object_array_entry *entry)
 +{
 +      return entry->path ? entry->path : entry->name;
 +}
 +
  static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
                         const struct object_id *old_oid,
                         const struct object_id *new_oid,
                         int old_oid_valid,
                         int new_oid_valid,
 -                       const char *old_name,
 -                       const char *new_name)
 +                       const char *old_path,
 +                       const char *new_path)
  {
        struct diff_filespec *one, *two;
  
        if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
                SWAP(old_mode, new_mode);
                SWAP(old_oid, new_oid);
 -              SWAP(old_name, new_name);
 +              SWAP(old_path, new_path);
        }
  
        if (opt->prefix &&
 -          (strncmp(old_name, opt->prefix, opt->prefix_length) ||
 -           strncmp(new_name, opt->prefix, opt->prefix_length)))
 +          (strncmp(old_path, opt->prefix, opt->prefix_length) ||
 +           strncmp(new_path, opt->prefix, opt->prefix_length)))
                return;
  
 -      one = alloc_filespec(old_name);
 -      two = alloc_filespec(new_name);
 +      one = alloc_filespec(old_path);
 +      two = alloc_filespec(new_path);
-       fill_filespec(one, old_oid->hash, old_oid_valid, old_mode);
-       fill_filespec(two, new_oid->hash, new_oid_valid, new_mode);
+       fill_filespec(one, old_oid, old_oid_valid, old_mode);
+       fill_filespec(two, new_oid, new_oid_valid, new_mode);
  
        diff_queue(&diff_queued_diff, one, two);
  }
  
  static int builtin_diff_b_f(struct rev_info *revs,
                            int argc, const char **argv,
 -                          struct blobinfo *blob)
 +                          struct object_array_entry **blob)
  {
        /* Blob vs file in the working tree*/
        struct stat st;
  
        diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
  
 -      if (blob[0].mode == S_IFINVALID)
 -              blob[0].mode = canon_mode(st.st_mode);
 +      if (blob[0]->mode == S_IFINVALID)
 +              blob[0]->mode = canon_mode(st.st_mode);
  
        stuff_change(&revs->diffopt,
 -                   blob[0].mode, canon_mode(st.st_mode),
 -                   &blob[0].oid, &null_oid,
 +                   blob[0]->mode, canon_mode(st.st_mode),
 +                   &blob[0]->item->oid, &null_oid,
                     1, 0,
 -                   path, path);
 +                   blob[0]->path ? blob[0]->path : path,
 +                   path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
  
  static int builtin_diff_blobs(struct rev_info *revs,
                              int argc, const char **argv,
 -                            struct blobinfo *blob)
 +                            struct object_array_entry **blob)
  {
        unsigned mode = canon_mode(S_IFREG | 0644);
  
        if (argc > 1)
                usage(builtin_diff_usage);
  
 -      if (blob[0].mode == S_IFINVALID)
 -              blob[0].mode = mode;
 +      if (blob[0]->mode == S_IFINVALID)
 +              blob[0]->mode = mode;
  
 -      if (blob[1].mode == S_IFINVALID)
 -              blob[1].mode = mode;
 +      if (blob[1]->mode == S_IFINVALID)
 +              blob[1]->mode = mode;
  
        stuff_change(&revs->diffopt,
 -                   blob[0].mode, blob[1].mode,
 -                   &blob[0].oid, &blob[1].oid,
 +                   blob[0]->mode, blob[1]->mode,
 +                   &blob[0]->item->oid, &blob[1]->item->oid,
                     1, 1,
 -                   blob[0].name, blob[1].name);
 +                   blob_path(blob[0]), blob_path(blob[1]));
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
@@@ -174,7 -174,7 +174,7 @@@ static int builtin_diff_tree(struct rev
                swap = 1;
        oid[swap] = &ent0->item->oid;
        oid[1 - swap] = &ent1->item->oid;
-       diff_tree_sha1(oid[0]->hash, oid[1]->hash, "", &revs->diffopt);
+       diff_tree_oid(oid[0], oid[1], "", &revs->diffopt);
        log_tree_diff_flush(revs);
        return 0;
  }
@@@ -194,7 -194,7 +194,7 @@@ static int builtin_diff_combined(struc
                revs->dense_combined_merges = revs->combine_merges = 1;
        for (i = 1; i < ents; i++)
                oid_array_append(&parents, &ent[i].item->oid);
-       diff_tree_combined(ent[0].item->oid.hash, &parents,
+       diff_tree_combined(&ent[0].item->oid, &parents,
                           revs->dense_combined_merges, revs);
        oid_array_clear(&parents);
        return 0;
@@@ -259,7 -259,7 +259,7 @@@ int cmd_diff(int argc, const char **arg
        struct rev_info rev;
        struct object_array ent = OBJECT_ARRAY_INIT;
        int blobs = 0, paths = 0;
 -      struct blobinfo blob[2];
 +      struct object_array_entry *blob[2];
        int nongit = 0, no_index = 0;
        int result = 0;
  
                } else if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
                                die(_("more than two blobs given: '%s'"), name);
 -                      oidcpy(&blob[blobs].oid, &obj->oid);
 -                      blob[blobs].name = name;
 -                      blob[blobs].mode = entry->mode;
 +                      blob[blobs] = entry;
                        blobs++;
  
                } else {
diff --combined builtin/fast-export.c
index 2dfed874546efc1135e09199dbfb7f803d02d91a,d57f36c43844cefcfa9302de4c3118f156f1383d..a932be04f4d5f733eebe1ebeba82c1bc64de3088
@@@ -562,12 -562,12 +562,12 @@@ static void handle_commit(struct commi
            get_object_mark(&commit->parents->item->object) != 0 &&
            !full_tree) {
                parse_commit_or_die(commit->parents->item);
-               diff_tree_sha1(commit->parents->item->tree->object.oid.hash,
-                              commit->tree->object.oid.hash, "", &rev->diffopt);
+               diff_tree_oid(&commit->parents->item->tree->object.oid,
+                             &commit->tree->object.oid, "", &rev->diffopt);
        }
        else
-               diff_root_tree_sha1(commit->tree->object.oid.hash,
-                                   "", &rev->diffopt);
+               diff_root_tree_oid(&commit->tree->object.oid,
+                                  "", &rev->diffopt);
  
        /* Export the referenced blobs, and remember the marks. */
        for (i = 0; i < diff_queued_diff.nr; i++)
@@@ -907,7 -907,9 +907,7 @@@ static void export_marks(char *file
  static void import_marks(char *input_file)
  {
        char line[512];
 -      FILE *f = fopen(input_file, "r");
 -      if (!f)
 -              die_errno("cannot read '%s'", input_file);
 +      FILE *f = xfopen(input_file, "r");
  
        while (fgets(line, sizeof(line), f)) {
                uint32_t mark;
diff --combined builtin/grep.c
index 26d43b4e4cdf56d621e9618a60c6b72dfe868b09,623c13a93986d7f5524b509cf2ac197f3944baa5..3e4b9600e86b661c99f4b936dfa574029ac4fbba
@@@ -73,14 -73,14 +73,14 @@@ static pthread_mutex_t grep_mutex
  
  static inline void grep_lock(void)
  {
 -      if (num_threads)
 -              pthread_mutex_lock(&grep_mutex);
 +      assert(num_threads);
 +      pthread_mutex_lock(&grep_mutex);
  }
  
  static inline void grep_unlock(void)
  {
 -      if (num_threads)
 -              pthread_mutex_unlock(&grep_mutex);
 +      assert(num_threads);
 +      pthread_mutex_unlock(&grep_mutex);
  }
  
  /* Signalled when a new work_item is added to todo. */
@@@ -224,8 -224,7 +224,8 @@@ static void start_threads(struct grep_o
                int err;
                struct grep_opt *o = grep_opt_dup(opt);
                o->output = strbuf_out;
 -              o->debug = 0;
 +              if (i)
 +                      o->debug = 0;
                compile_grep_patterns(o);
                err = pthread_create(&threads[i], NULL, run, o);
  
@@@ -290,22 -289,8 +290,22 @@@ static int grep_cmd_config(const char *
                if (num_threads < 0)
                        die(_("invalid number of threads specified (%d) for %s"),
                            num_threads, var);
 +#ifdef NO_PTHREADS
 +              else if (num_threads && num_threads != 1) {
 +                      /*
 +                       * TRANSLATORS: %s is the configuration
 +                       * variable for tweaking threads, currently
 +                       * grep.threads
 +                       */
 +                      warning(_("no threads support, ignoring %s"), var);
 +                      num_threads = 0;
 +              }
 +#endif
        }
  
 +      if (!strcmp(var, "submodule.recurse"))
 +              recurse_submodules = git_config_bool(var, value);
 +
        return st;
  }
  
@@@ -342,7 -327,7 +342,7 @@@ static int grep_oid(struct grep_opt *op
  
  #ifndef NO_PTHREADS
        if (num_threads) {
-               add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
+               add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                return 0;
        } else
                struct grep_source gs;
                int hit;
  
-               grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
+               grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                hit = grep_source(opt, &gs);
  
@@@ -510,8 -495,6 +510,8 @@@ static void compile_submodule_options(c
                break;
        case GREP_PATTERN_TYPE_UNSPECIFIED:
                break;
 +      default:
 +              die("BUG: Added a new grep pattern type without updating switch statement");
        }
  
        for (pattern = opt->pattern_list; pattern != NULL;
@@@ -587,7 -570,7 +587,7 @@@ static int grep_submodule_launch(struc
         * with the object's name: 'tree-name:filename'.  In order to
         * provide uniformity of output we want to pass the name of the
         * parent project's object name to the submodule so the submodule can
-        * prefix its output with the parent's name and not its own SHA1.
+        * prefix its output with the parent's name and not its own OID.
         */
        if (gs->identifier && end_of_base)
                argv_array_pushf(&cp.args, "--parent-basename=%.*s",
                 * If there is a tree identifier for the submodule, add the
                 * rev after adding the submodule options but before the
                 * pathspecs.  To do this we listen for the '--' and insert the
-                * sha1 before pushing the '--' onto the child process argv
+                * oid before pushing the '--' onto the child process argv
                 * array.
                 */
                if (gs->identifier &&
                    !strcmp("--", submodule_options.argv[i])) {
-                       argv_array_push(&cp.args, sha1_to_hex(gs->identifier));
+                       argv_array_push(&cp.args, oid_to_hex(gs->identifier));
                }
  
                argv_array_push(&cp.args, submodule_options.argv[i]);
  
  /*
   * Prep grep structures for a submodule grep
-  * sha1: the sha1 of the submodule or NULL if using the working tree
+  * oid: the oid of the submodule or NULL if using the working tree
   * filename: name of the submodule including tree name of parent
   * path: location of the submodule
   */
- static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
+ static int grep_submodule(struct grep_opt *opt, const struct object_id *oid,
                          const char *filename, const char *path)
  {
        if (!is_submodule_initialized(path))
                 * If searching history, check for the presense of the
                 * submodule's gitdir before skipping the submodule.
                 */
-               if (sha1) {
+               if (oid) {
                        const struct submodule *sub =
                                        submodule_from_path(null_sha1, path);
                        if (sub)
  
  #ifndef NO_PTHREADS
        if (num_threads) {
-               add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, sha1);
+               add_work(opt, GREP_SOURCE_SUBMODULE, filename, path, oid);
                return 0;
        } else
  #endif
                int hit;
  
                grep_source_init(&gs, GREP_SOURCE_SUBMODULE,
-                                filename, path, sha1);
+                                filename, path, oid);
                hit = grep_submodule_launch(opt, &gs);
  
                grep_source_clear(&gs);
@@@ -792,7 -775,7 +792,7 @@@ static int grep_tree(struct grep_opt *o
                                         check_attr);
                        free(data);
                } else if (recurse_submodules && S_ISGITLINK(entry.mode)) {
-                       hit |= grep_submodule(opt, entry.oid->hash, base->buf,
+                       hit |= grep_submodule(opt, entry.oid, base->buf,
                                              base->buf + tn_len);
                }
  
@@@ -1171,6 -1154,8 +1171,6 @@@ int cmd_grep(int argc, const char **arg
        if (!opt.fixed && opt.ignore_case)
                opt.regflags |= REG_ICASE;
  
 -      compile_grep_patterns(&opt);
 -
        /*
         * We have to find "--" in a separate pass, because its presence
         * influences how we will parse arguments that come before it.
                        break;
                }
  
 -              if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
 +              if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH,
 +                                        oid.hash, &oc)) {
                        if (seen_dashdash)
                                die(_("unable to resolve revision: %s"), arg);
                        break;
                if (!seen_dashdash)
                        verify_non_filename(prefix, arg);
                add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
 +              free(oc.path);
        }
  
        /*
                num_threads = GREP_NUM_THREADS_DEFAULT;
        else if (num_threads < 0)
                die(_("invalid number of threads specified (%d)"), num_threads);
 +      if (num_threads == 1)
 +              num_threads = 0;
  #else
 +      if (num_threads)
 +              warning(_("no threads support, ignoring --threads"));
        num_threads = 0;
  #endif
  
 +      if (!num_threads)
 +              /*
 +               * The compiled patterns on the main path are only
 +               * used when not using threading. Otherwise
 +               * start_threads() below calls compile_grep_patterns()
 +               * for each thread.
 +               */
 +              compile_grep_patterns(&opt);
 +
  #ifndef NO_PTHREADS
        if (num_threads) {
                if (!(opt.name_only || opt.unmatch_name_only || opt.count)
diff --combined builtin/log.c
index a33c1a70abe1c01ae408bf23d01a3d1d455f1f8e,4ef522ee50030f81d8eaf73b4a87793a4242a46d..998437b23dcb32a45e4d722d695c86d3cc82b4bc
@@@ -483,20 -483,16 +483,20 @@@ static int show_blob_object(const struc
            !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
                return stream_blob_to_fd(1, oid, NULL, 0);
  
 -      if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context))
 +      if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
 +                                oidc.hash, &obj_context))
                die(_("Not a valid object name %s"), obj_name);
 -      if (!obj_context.path[0] ||
 -          !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size))
 +      if (!obj_context.path ||
 +          !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
 +              free(obj_context.path);
                return stream_blob_to_fd(1, oid, NULL, 0);
 +      }
  
        if (!buf)
                die(_("git show %s: bad file"), obj_name);
  
        write_or_die(1, buf, size);
 +      free(obj_context.path);
        return 0;
  }
  
@@@ -846,10 -842,8 +846,10 @@@ static int open_next_file(struct commi
        if (output_directory) {
                strbuf_addstr(&filename, output_directory);
                if (filename.len >=
 -                  PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
 +                  PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len) {
 +                      strbuf_release(&filename);
                        return error(_("name of output directory is too long"));
 +              }
                strbuf_complete(&filename, '/');
        }
  
        if (!quiet)
                printf("%s\n", filename.buf + outdir_offset);
  
 -      if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL)
 -              return error(_("Cannot open patch file %s"), filename.buf);
 +      if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) {
 +              error_errno(_("Cannot open patch file %s"), filename.buf);
 +              strbuf_release(&filename);
 +              return -1;
 +      }
  
        strbuf_release(&filename);
        return 0;
@@@ -1052,9 -1043,9 +1052,9 @@@ static void make_cover_letter(struct re
  
        diff_setup_done(&opts);
  
-       diff_tree_sha1(origin->tree->object.oid.hash,
-                      head->tree->object.oid.hash,
-                      "", &opts);
+       diff_tree_oid(&origin->tree->object.oid,
+                     &head->tree->object.oid,
+                     "", &opts);
        diffcore_std(&opts);
        diff_flush(&opts);
  
@@@ -1363,7 -1354,7 +1363,7 @@@ static void prepare_bases(struct base_t
                struct object_id *patch_id;
                if (commit->util)
                        continue;
-               if (commit_patch_id(commit, &diffopt, oid.hash, 0))
+               if (commit_patch_id(commit, &diffopt, &oid, 0))
                        die(_("cannot get patch id"));
                ALLOC_GROW(bases->patch_id, bases->nr_patch_id + 1, bases->alloc_patch_id);
                patch_id = bases->patch_id + bases->nr_patch_id;
diff --combined builtin/merge.c
index eab03a026d26aaf6b42fb43a363b67857660a763,afaed6a2c2fa4d426e440b2bdcc7bb0461253449..84970cd85e8b0788928ff24b47c8c1b3dc3409e2
@@@ -415,7 -415,7 +415,7 @@@ static void finish(struct commit *head_
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
                diff_setup_done(&opts);
-               diff_tree_sha1(head->hash, new_head->hash, "", &opts);
+               diff_tree_oid(head, new_head, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
        }
@@@ -839,7 -839,9 +839,7 @@@ static int suggest_conflicts(void
        struct strbuf msgbuf = STRBUF_INIT;
  
        filename = git_path_merge_msg();
 -      fp = fopen(filename, "a");
 -      if (!fp)
 -              die_errno(_("Could not open '%s' for writing"), filename);
 +      fp = xfopen(filename, "a");
  
        append_conflicts_hint(&msgbuf);
        fputs(msgbuf.buf, fp);
diff --combined builtin/notes.c
index 7196bff0eb2454eee86f87241706523c0266e66e,2ebc2b7c43f433648f1156129f209747fbd6e184..c939a84b7629cd5d4dda09d427255410d723d95b
@@@ -109,11 -109,11 +109,11 @@@ static void free_note_data(struct note_
        strbuf_release(&d->buf);
  }
  
- static int list_each_note(const unsigned char *object_sha1,
-               const unsigned char *note_sha1, char *note_path,
+ static int list_each_note(const struct object_id *object_oid,
+               const struct object_id *note_oid, char *note_path,
                void *cb_data)
  {
-       printf("%s %s\n", sha1_to_hex(note_sha1), sha1_to_hex(object_sha1));
+       printf("%s %s\n", oid_to_hex(note_oid), oid_to_hex(object_oid));
        return 0;
  }
  
@@@ -129,10 -129,10 +129,10 @@@ static void copy_obj_to_fd(int fd, cons
        }
  }
  
- static void write_commented_object(int fd, const unsigned char *object)
+ static void write_commented_object(int fd, const struct object_id *object)
  {
        const char *show_args[5] =
-               {"show", "--stat", "--no-notes", sha1_to_hex(object), NULL};
+               {"show", "--stat", "--no-notes", oid_to_hex(object), NULL};
        struct child_process show = CHILD_PROCESS_INIT;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf cbuf = STRBUF_INIT;
        show.git_cmd = 1;
        if (start_command(&show))
                die(_("unable to start 'show' for object '%s'"),
-                   sha1_to_hex(object));
+                   oid_to_hex(object));
  
        if (strbuf_read(&buf, show.out, 0) < 0)
                die_errno(_("could not read 'show' output"));
  
        if (finish_command(&show))
                die(_("failed to finish 'show' for object '%s'"),
-                   sha1_to_hex(object));
+                   oid_to_hex(object));
  }
  
- static void prepare_note_data(const unsigned char *object, struct note_data *d,
+ static void prepare_note_data(const struct object_id *object, struct note_data *d,
                const unsigned char *old_note)
  {
        if (d->use_editor || !d->given) {
@@@ -243,16 -243,16 +243,16 @@@ static int parse_reuse_arg(const struc
  {
        struct note_data *d = opt->value;
        char *buf;
-       unsigned char object[20];
+       struct object_id object;
        enum object_type type;
        unsigned long len;
  
        if (d->buf.len)
                strbuf_addch(&d->buf, '\n');
  
-       if (get_sha1(arg, object))
+       if (get_oid(arg, &object))
                die(_("failed to resolve '%s' as a valid ref."), arg);
-       if (!(buf = read_sha1_file(object, &type, &len))) {
+       if (!(buf = read_sha1_file(object.hash, &type, &len))) {
                free(buf);
                die(_("failed to read object '%s'."), arg);
        }
@@@ -292,7 -292,7 +292,7 @@@ static int notes_copy_from_stdin(int fo
        }
  
        while (strbuf_getline_lf(&buf, stdin) != EOF) {
-               unsigned char from_obj[20], to_obj[20];
+               struct object_id from_obj, to_obj;
                struct strbuf **split;
                int err;
  
                        die(_("malformed input line: '%s'."), buf.buf);
                strbuf_rtrim(split[0]);
                strbuf_rtrim(split[1]);
-               if (get_sha1(split[0]->buf, from_obj))
+               if (get_oid(split[0]->buf, &from_obj))
                        die(_("failed to resolve '%s' as a valid ref."), split[0]->buf);
-               if (get_sha1(split[1]->buf, to_obj))
+               if (get_oid(split[1]->buf, &to_obj))
                        die(_("failed to resolve '%s' as a valid ref."), split[1]->buf);
  
                if (rewrite_cmd)
-                       err = copy_note_for_rewrite(c, from_obj, to_obj);
+                       err = copy_note_for_rewrite(c, &from_obj, &to_obj);
                else
-                       err = copy_note(t, from_obj, to_obj, force,
+                       err = copy_note(t, &from_obj, &to_obj, force,
                                        combine_notes_overwrite);
  
                if (err) {
@@@ -340,10 -340,8 +340,10 @@@ static struct notes_tree *init_notes_ch
  
        ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref;
        if (!starts_with(ref, "refs/notes/"))
 -              /* TRANSLATORS: the first %s will be replaced by a
 -                 git notes command: 'add', 'merge', 'remove', etc.*/
 +              /*
 +               * TRANSLATORS: the first %s will be replaced by a git
 +               * notes command: 'add', 'merge', 'remove', etc.
 +               */
                die(_("refusing to %s notes in %s (outside of refs/notes/)"),
                    subcommand, ref);
        return t;
  static int list(int argc, const char **argv, const char *prefix)
  {
        struct notes_tree *t;
-       unsigned char object[20];
-       const unsigned char *note;
+       struct object_id object;
+       const struct object_id *note;
        int retval = -1;
        struct option options[] = {
                OPT_END()
  
        t = init_notes_check("list", 0);
        if (argc) {
-               if (get_sha1(argv[0], object))
+               if (get_oid(argv[0], &object))
                        die(_("failed to resolve '%s' as a valid ref."), argv[0]);
-               note = get_note(t, object);
+               note = get_note(t, &object);
                if (note) {
-                       puts(sha1_to_hex(note));
+                       puts(oid_to_hex(note));
                        retval = 0;
                } else
                        retval = error(_("no note found for object %s."),
-                                      sha1_to_hex(object));
+                                      oid_to_hex(&object));
        } else
                retval = for_each_note(t, 0, list_each_note, NULL);
  
@@@ -393,8 -391,8 +393,8 @@@ static int add(int argc, const char **a
        int force = 0, allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
-       unsigned char object[20], new_note[20];
-       const unsigned char *note;
+       struct object_id object, new_note;
+       const struct object_id *note;
        struct note_data d = { 0, 0, NULL, STRBUF_INIT };
        struct option options[] = {
                { OPTION_CALLBACK, 'm', "message", &d, N_("message"),
  
        object_ref = argc > 1 ? argv[1] : "HEAD";
  
-       if (get_sha1(object_ref, object))
+       if (get_oid(object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
  
        t = init_notes_check("add", NOTES_INIT_WRITABLE);
-       note = get_note(t, object);
+       note = get_note(t, &object);
  
        if (note) {
                if (!force) {
                                return error(_("Cannot add notes. "
                                        "Found existing notes for object %s. "
                                        "Use '-f' to overwrite existing notes"),
-                                       sha1_to_hex(object));
+                                       oid_to_hex(&object));
                        }
                        /*
                         * Redirect to "edit" subcommand.
                        return append_edit(argc, argv, prefix);
                }
                fprintf(stderr, _("Overwriting existing notes for object %s\n"),
-                       sha1_to_hex(object));
+                       oid_to_hex(&object));
        }
  
-       prepare_note_data(object, &d, note);
+       prepare_note_data(&object, &d, note->hash);
        if (d.buf.len || allow_empty) {
-               write_note_data(&d, new_note);
-               if (add_note(t, object, new_note, combine_notes_overwrite))
+               write_note_data(&d, new_note.hash);
+               if (add_note(t, &object, &new_note, combine_notes_overwrite))
                        die("BUG: combine_notes_overwrite failed");
                commit_notes(t, "Notes added by 'git notes add'");
        } else {
                fprintf(stderr, _("Removing note for object %s\n"),
-                       sha1_to_hex(object));
-               remove_note(t, object);
+                       oid_to_hex(&object));
+               remove_note(t, object.hash);
                commit_notes(t, "Notes removed by 'git notes add'");
        }
  
  static int copy(int argc, const char **argv, const char *prefix)
  {
        int retval = 0, force = 0, from_stdin = 0;
-       const unsigned char *from_note, *note;
+       const struct object_id *from_note, *note;
        const char *object_ref;
-       unsigned char object[20], from_obj[20];
+       struct object_id object, from_obj;
        struct notes_tree *t;
        const char *rewrite_cmd = NULL;
        struct option options[] = {
                usage_with_options(git_notes_copy_usage, options);
        }
  
-       if (get_sha1(argv[0], from_obj))
+       if (get_oid(argv[0], &from_obj))
                die(_("failed to resolve '%s' as a valid ref."), argv[0]);
  
        object_ref = 1 < argc ? argv[1] : "HEAD";
  
-       if (get_sha1(object_ref, object))
+       if (get_oid(object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
  
        t = init_notes_check("copy", NOTES_INIT_WRITABLE);
-       note = get_note(t, object);
+       note = get_note(t, &object);
  
        if (note) {
                if (!force) {
                        retval = error(_("Cannot copy notes. Found existing "
                                       "notes for object %s. Use '-f' to "
                                       "overwrite existing notes"),
-                                      sha1_to_hex(object));
+                                      oid_to_hex(&object));
                        goto out;
                }
                fprintf(stderr, _("Overwriting existing notes for object %s\n"),
-                       sha1_to_hex(object));
+                       oid_to_hex(&object));
        }
  
-       from_note = get_note(t, from_obj);
+       from_note = get_note(t, &from_obj);
        if (!from_note) {
                retval = error(_("missing notes on source object %s. Cannot "
-                              "copy."), sha1_to_hex(from_obj));
+                              "copy."), oid_to_hex(&from_obj));
                goto out;
        }
  
-       if (add_note(t, object, from_note, combine_notes_overwrite))
+       if (add_note(t, &object, from_note, combine_notes_overwrite))
                die("BUG: combine_notes_overwrite failed");
        commit_notes(t, "Notes added by 'git notes copy'");
  out:
@@@ -554,8 -552,8 +554,8 @@@ static int append_edit(int argc, const 
        int allow_empty = 0;
        const char *object_ref;
        struct notes_tree *t;
-       unsigned char object[20], new_note[20];
-       const unsigned char *note;
+       struct object_id object, new_note;
+       const struct object_id *note;
        char *logmsg;
        const char * const *usage;
        struct note_data d = { 0, 0, NULL, STRBUF_INIT };
  
        object_ref = 1 < argc ? argv[1] : "HEAD";
  
-       if (get_sha1(object_ref, object))
+       if (get_oid(object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
  
        t = init_notes_check(argv[0], NOTES_INIT_WRITABLE);
-       note = get_note(t, object);
+       note = get_note(t, &object);
  
-       prepare_note_data(object, &d, edit ? note : NULL);
+       prepare_note_data(&object, &d, edit && note ? note->hash : NULL);
  
        if (note && !edit) {
                /* Append buf to previous note contents */
                unsigned long size;
                enum object_type type;
-               char *prev_buf = read_sha1_file(note, &type, &size);
+               char *prev_buf = read_sha1_file(note->hash, &type, &size);
  
                strbuf_grow(&d.buf, size + 1);
                if (d.buf.len && prev_buf && size)
        }
  
        if (d.buf.len || allow_empty) {
-               write_note_data(&d, new_note);
-               if (add_note(t, object, new_note, combine_notes_overwrite))
+               write_note_data(&d, new_note.hash);
+               if (add_note(t, &object, &new_note, combine_notes_overwrite))
                        die("BUG: combine_notes_overwrite failed");
                logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
        } else {
                fprintf(stderr, _("Removing note for object %s\n"),
-                       sha1_to_hex(object));
-               remove_note(t, object);
+                       oid_to_hex(&object));
+               remove_note(t, object.hash);
                logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]);
        }
        commit_notes(t, logmsg);
@@@ -639,8 -637,8 +639,8 @@@ static int show(int argc, const char **
  {
        const char *object_ref;
        struct notes_tree *t;
-       unsigned char object[20];
-       const unsigned char *note;
+       struct object_id object;
+       const struct object_id *note;
        int retval;
        struct option options[] = {
                OPT_END()
  
        object_ref = argc ? argv[0] : "HEAD";
  
-       if (get_sha1(object_ref, object))
+       if (get_oid(object_ref, &object))
                die(_("failed to resolve '%s' as a valid ref."), object_ref);
  
        t = init_notes_check("show", 0);
-       note = get_note(t, object);
+       note = get_note(t, &object);
  
        if (!note)
                retval = error(_("no note found for object %s."),
-                              sha1_to_hex(object));
+                              oid_to_hex(&object));
        else {
-               const char *show_args[3] = {"show", sha1_to_hex(note), NULL};
+               const char *show_args[3] = {"show", oid_to_hex(note), NULL};
                retval = execv_git_cmd(show_args);
        }
        free_notes(t);
@@@ -726,7 -724,7 +726,7 @@@ static int merge_commit(struct notes_me
        if (!o->local_ref)
                die(_("failed to resolve NOTES_MERGE_REF"));
  
-       if (notes_merge_commit(o, t, partial, oid.hash))
+       if (notes_merge_commit(o, t, partial, &oid))
                die(_("failed to finalize notes merge"));
  
        /* Reuse existing commit message in reflog message */
@@@ -762,7 -760,7 +762,7 @@@ static int git_config_get_notes_strateg
  static int merge(int argc, const char **argv, const char *prefix)
  {
        struct strbuf remote_ref = STRBUF_INIT, msg = STRBUF_INIT;
-       unsigned char result_sha1[20];
+       struct object_id result_oid;
        struct notes_tree *t;
        struct notes_merge_options o;
        int do_merge = 0, do_commit = 0, do_abort = 0;
                    remote_ref.buf, default_notes_ref());
        strbuf_add(&(o.commit_msg), msg.buf + 7, msg.len - 7); /* skip "notes: " */
  
-       result = notes_merge(&o, t, result_sha1);
+       result = notes_merge(&o, t, &result_oid);
  
-       if (result >= 0) /* Merge resulted (trivially) in result_sha1 */
+       if (result >= 0) /* Merge resulted (trivially) in result_oid */
                /* Update default notes ref with new commit */
-               update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
+               update_ref(msg.buf, default_notes_ref(), result_oid.hash, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
        else { /* Merge has unresolved conflicts */
                const struct worktree *wt;
                /* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
-               update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
+               update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_oid.hash, NULL,
                           0, UPDATE_REFS_DIE_ON_ERR);
                /* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
                wt = find_shared_symref("NOTES_MERGE_REF", default_notes_ref());
  static int remove_one_note(struct notes_tree *t, const char *name, unsigned flag)
  {
        int status;
-       unsigned char sha1[20];
-       if (get_sha1(name, sha1))
+       struct object_id oid;
+       if (get_oid(name, &oid))
                return error(_("Failed to resolve '%s' as a valid ref."), name);
-       status = remove_note(t, sha1);
+       status = remove_note(t, oid.hash);
        if (status)
                fprintf(stderr, _("Object %s has no note\n"), name);
        else
diff --combined cache.h
index 4d92aae0e818015c1ec7a6d6bc99cb1391e0d824,50fd2b3ccfd56b5a298f9e47dafb903cb82e26ba..d6ba8a2f11a63d4d9ec5257a75532414d22526c0
+++ b/cache.h
@@@ -1026,6 -1026,13 +1026,13 @@@ static inline void oidcpy(struct object
        hashcpy(dst->hash, src->hash);
  }
  
+ static inline struct object_id *oiddup(const struct object_id *src)
+ {
+       struct object_id *dst = xmalloc(sizeof(struct object_id));
+       oidcpy(dst, src);
+       return dst;
+ }
  static inline void hashclr(unsigned char *hash)
  {
        memset(hash, 0, GIT_SHA1_RAWSZ);
@@@ -1334,18 -1341,13 +1341,18 @@@ static inline int hex2chr(const char *s
  
  struct object_context {
        unsigned char tree[20];
 -      char path[PATH_MAX];
        unsigned mode;
        /*
         * symlink_path is only used by get_tree_entry_follow_symlinks,
         * and only for symlinks that point outside the repository.
         */
        struct strbuf symlink_path;
 +      /*
 +       * If GET_SHA1_RECORD_PATH is set, this will record path (if any)
 +       * found when resolving the name. The caller is responsible for
 +       * releasing the memory.
 +       */
 +      char *path;
  };
  
  #define GET_SHA1_QUIETLY           01
  #define GET_SHA1_TREEISH          020
  #define GET_SHA1_BLOB             040
  #define GET_SHA1_FOLLOW_SYMLINKS 0100
 +#define GET_SHA1_RECORD_PATH     0200
  #define GET_SHA1_ONLY_TO_DIE    04000
  
  #define GET_SHA1_DISAMBIGUATORS \
@@@ -1370,7 -1371,7 +1377,7 @@@ extern int get_sha1_tree(const char *st
  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);
 +extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc);
  
  extern int get_oid(const char *str, struct object_id *oid);
  
diff --combined diff-lib.c
index 76c8f185cd032b92e010b95e4d92e3b08a6ab18a,9e076a48861a9be8f8fe9d17e6fa18197fc49fe1..0c0e20f7c004119cd86bc377722eca6d318cdf5d
@@@ -29,7 -29,7 +29,7 @@@
  static int check_removed(const struct cache_entry *ce, struct stat *st)
  {
        if (lstat(ce->name, st) < 0) {
 -              if (errno != ENOENT && errno != ENOTDIR)
 +              if (!is_missing_file_error(errno))
                        return -1;
                return 1;
        }
@@@ -101,7 -101,7 +101,7 @@@ int run_diff_files(struct rev_info *rev
                struct cache_entry *ce = active_cache[i];
                int changed;
                unsigned dirty_submodule = 0;
-               const unsigned char *old_sha1, *new_sha1;
+               const struct object_id *old_oid, *new_oid;
  
                if (diff_can_quit_early(&revs->diffopt))
                        break;
                                        continue;
                                }
                                diff_addremove(&revs->diffopt, '-', ce->ce_mode,
-                                              ce->oid.hash,
+                                              &ce->oid,
                                               !is_null_oid(&ce->oid),
                                               ce->name, 0);
                                continue;
                        } else if (revs->diffopt.ita_invisible_in_index &&
                                   ce_intent_to_add(ce)) {
                                diff_addremove(&revs->diffopt, '+', ce->ce_mode,
-                                              EMPTY_BLOB_SHA1_BIN, 0,
+                                              &empty_tree_oid, 0,
                                               ce->name, 0);
                                continue;
                        }
                                continue;
                }
                oldmode = ce->ce_mode;
-               old_sha1 = ce->oid.hash;
-               new_sha1 = changed ? null_sha1 : ce->oid.hash;
+               old_oid = &ce->oid;
+               new_oid = changed ? &null_oid : &ce->oid;
                diff_change(&revs->diffopt, oldmode, newmode,
-                           old_sha1, new_sha1,
-                           !is_null_sha1(old_sha1),
-                           !is_null_sha1(new_sha1),
+                           old_oid, new_oid,
+                           !is_null_oid(old_oid),
+                           !is_null_oid(new_oid),
                            ce->name, 0, dirty_submodule);
  
        }
  static void diff_index_show_file(struct rev_info *revs,
                                 const char *prefix,
                                 const struct cache_entry *ce,
-                                const unsigned char *sha1, int sha1_valid,
+                                const struct object_id *oid, int oid_valid,
                                 unsigned int mode,
                                 unsigned dirty_submodule)
  {
        diff_addremove(&revs->diffopt, prefix[0], mode,
-                      sha1, sha1_valid, ce->name, dirty_submodule);
+                      oid, oid_valid, ce->name, dirty_submodule);
  }
  
  static int get_stat_data(const struct cache_entry *ce,
-                        const unsigned char **sha1p,
+                        const struct object_id **oidp,
                         unsigned int *modep,
                         int cached, int match_missing,
                         unsigned *dirty_submodule, struct diff_options *diffopt)
  {
-       const unsigned char *sha1 = ce->oid.hash;
+       const struct object_id *oid = &ce->oid;
        unsigned int mode = ce->ce_mode;
  
        if (!cached && !ce_uptodate(ce)) {
                        return -1;
                else if (changed) {
                        if (match_missing) {
-                               *sha1p = sha1;
+                               *oidp = oid;
                                *modep = mode;
                                return 0;
                        }
                                                    0, dirty_submodule);
                if (changed) {
                        mode = ce_mode_from_stat(ce, st.st_mode);
-                       sha1 = null_sha1;
+                       oid = &null_oid;
                }
        }
  
-       *sha1p = sha1;
+       *oidp = oid;
        *modep = mode;
        return 0;
  }
@@@ -303,7 -303,7 +303,7 @@@ static void show_new_file(struct rev_in
                          const struct cache_entry *new,
                          int cached, int match_missing)
  {
-       const unsigned char *sha1;
+       const struct object_id *oid;
        unsigned int mode;
        unsigned dirty_submodule = 0;
  
         * New file in the index: it might actually be different in
         * the working tree.
         */
-       if (get_stat_data(new, &sha1, &mode, cached, match_missing,
+       if (get_stat_data(new, &oid, &mode, cached, match_missing,
            &dirty_submodule, &revs->diffopt) < 0)
                return;
  
-       diff_index_show_file(revs, "+", new, sha1, !is_null_sha1(sha1), mode, dirty_submodule);
+       diff_index_show_file(revs, "+", new, oid, !is_null_oid(oid), mode, dirty_submodule);
  }
  
  static int show_modified(struct rev_info *revs,
                         int cached, int match_missing)
  {
        unsigned int mode, oldmode;
-       const unsigned char *sha1;
+       const struct object_id *oid;
        unsigned dirty_submodule = 0;
  
-       if (get_stat_data(new, &sha1, &mode, cached, match_missing,
+       if (get_stat_data(new, &oid, &mode, cached, match_missing,
                          &dirty_submodule, &revs->diffopt) < 0) {
                if (report_missing)
                        diff_index_show_file(revs, "-", old,
-                                            old->oid.hash, 1, old->ce_mode,
+                                            &old->oid, 1, old->ce_mode,
                                             0);
                return -1;
        }
  
        if (revs->combine_merges && !cached &&
-           (hashcmp(sha1, old->oid.hash) || oidcmp(&old->oid, &new->oid))) {
+           (oidcmp(oid, &old->oid) || oidcmp(&old->oid, &new->oid))) {
                struct combine_diff_path *p;
                int pathlen = ce_namelen(new);
  
        }
  
        oldmode = old->ce_mode;
-       if (mode == oldmode && !hashcmp(sha1, old->oid.hash) && !dirty_submodule &&
+       if (mode == oldmode && !oidcmp(oid, &old->oid) && !dirty_submodule &&
            !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
                return 0;
  
        diff_change(&revs->diffopt, oldmode, mode,
-                   old->oid.hash, sha1, 1, !is_null_sha1(sha1),
+                   &old->oid, oid, 1, !is_null_oid(oid),
                    old->name, 0, dirty_submodule);
        return 0;
  }
@@@ -409,7 -409,7 +409,7 @@@ static void do_oneway_diff(struct unpac
                struct diff_filepair *pair;
                pair = diff_unmerge(&revs->diffopt, idx->name);
                if (tree)
-                       fill_filespec(pair->one, tree->oid.hash, 1,
+                       fill_filespec(pair->one, &tree->oid, 1,
                                      tree->ce_mode);
                return;
        }
         * Something removed from the tree?
         */
        if (!idx) {
-               diff_index_show_file(revs, "-", tree, tree->oid.hash, 1,
+               diff_index_show_file(revs, "-", tree, &tree->oid, 1,
                                     tree->ce_mode, 0);
                return;
        }
diff --combined diff.c
index e35cf6c704505f4a56dc39f0dd39c6691acd1c9b,c758a0d73e3fc6e81796f02ddb45eb342167f4f1..acedf86aecc151112898ff54575151f157776281
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -27,7 -27,7 +27,7 @@@
  #endif
  
  static int diff_detect_rename_default;
 -static int diff_indent_heuristic; /* experimental */
 +static int diff_indent_heuristic = 1;
  static int diff_rename_limit_default = 400;
  static int diff_suppress_blank_empty;
  static int diff_use_color_default = -1;
@@@ -290,6 -290,9 +290,6 @@@ int git_diff_ui_config(const char *var
                return 0;
        }
  
 -      if (git_diff_heuristic_config(var, value, cb) < 0)
 -              return -1;
 -
        if (!strcmp(var, "diff.wserrorhighlight")) {
                int val = parse_ws_error_highlight(value);
                if (val < 0)
@@@ -348,9 -351,6 +348,9 @@@ int git_diff_basic_config(const char *v
        if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
  
 +      if (git_diff_heuristic_config(var, value, cb) < 0)
 +              return -1;
 +
        return git_default_config(var, value, cb);
  }
  
@@@ -2702,13 -2702,13 +2702,13 @@@ void free_filespec(struct diff_filespe
        }
  }
  
- void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
-                  int sha1_valid, unsigned short mode)
+ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,
+                  int oid_valid, unsigned short mode)
  {
        if (mode) {
                spec->mode = canon_mode(mode);
-               hashcpy(spec->oid.hash, sha1);
-               spec->oid_valid = sha1_valid;
+               oidcpy(&spec->oid, oid);
+               spec->oid_valid = oid_valid;
        }
  }
  
   * the work tree has that object contents, return true, so that
   * prepare_temp_file() does not have to inflate and extract.
   */
- static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file)
+ static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file)
  {
        const struct cache_entry *ce;
        struct stat st;
         * objects however would tend to be slower as they need
         * to be individually opened and inflated.
         */
-       if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
+       if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(oid->hash))
                return 0;
  
        /*
         * This is not the sha1 we are looking for, or
         * unreusable because it is not a regular file.
         */
-       if (hashcmp(sha1, ce->oid.hash) || !S_ISREG(ce->ce_mode))
+       if (oidcmp(oid, &ce->oid) || !S_ISREG(ce->ce_mode))
                return 0;
  
        /*
@@@ -2842,7 -2842,7 +2842,7 @@@ int diff_populate_filespec(struct diff_
                return diff_populate_gitlink(s, size_only);
  
        if (!s->oid_valid ||
-           reuse_worktree_file(s->path, s->oid.hash, 0)) {
+           reuse_worktree_file(s->path, &s->oid, 0)) {
                struct strbuf buf = STRBUF_INIT;
                struct stat st;
                int fd;
@@@ -3008,7 -3008,7 +3008,7 @@@ static struct diff_tempfile *prepare_te
  
        if (!S_ISGITLINK(one->mode) &&
            (!one->oid_valid ||
-            reuse_worktree_file(name, one->oid.hash, 1))) {
+            reuse_worktree_file(name, &one->oid, 1))) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
                        /* we can borrow from the file in the work tree */
                        temp->name = name;
                        if (!one->oid_valid)
-                               sha1_to_hex_r(temp->hex, null_sha1);
+                               oid_to_hex_r(temp->hex, &null_oid);
                        else
                                oid_to_hex_r(temp->hex, &one->oid);
                        /* Even though we may sometimes borrow the
                         * contents from the work tree, we always want
                         * one->mode.  mode is trustworthy even when
-                        * !(one->sha1_valid), as long as
+                        * !(one->oid_valid), as long as
                         * DIFF_FILE_VALID(one).
                         */
                        xsnprintf(temp->mode, sizeof(temp->mode), "%06o", one->mode);
@@@ -3239,7 -3239,7 +3239,7 @@@ static void run_diff_cmd(const char *pg
                fprintf(o->file, "* Unmerged path %s\n", name);
  }
  
- static void diff_fill_sha1_info(struct diff_filespec *one)
+ static void diff_fill_oid_info(struct diff_filespec *one)
  {
        if (DIFF_FILE_VALID(one)) {
                if (!one->oid_valid) {
@@@ -3298,8 -3298,8 +3298,8 @@@ static void run_diff(struct diff_filepa
                return;
        }
  
-       diff_fill_sha1_info(one);
-       diff_fill_sha1_info(two);
+       diff_fill_oid_info(one);
+       diff_fill_oid_info(two);
  
        if (!pgm &&
            DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
@@@ -3344,8 -3344,8 +3344,8 @@@ static void run_diffstat(struct diff_fi
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
  
-       diff_fill_sha1_info(p->one);
-       diff_fill_sha1_info(p->two);
+       diff_fill_oid_info(p->one);
+       diff_fill_oid_info(p->two);
  
        builtin_diffstat(name, other, p->one, p->two, diffstat, o, p);
  }
@@@ -3368,8 -3368,8 +3368,8 @@@ static void run_checkdiff(struct diff_f
        if (o->prefix_length)
                strip_prefix(o->prefix_length, &name, &other);
  
-       diff_fill_sha1_info(p->one);
-       diff_fill_sha1_info(p->two);
+       diff_fill_oid_info(p->one);
+       diff_fill_oid_info(p->two);
  
        builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
  }
@@@ -4071,7 -4071,9 +4071,7 @@@ int diff_opt_parse(struct diff_options 
                DIFF_OPT_CLR(options, FUNCCONTEXT);
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
                char *path = prefix_filename(prefix, optarg);
 -              options->file = fopen(path, "w");
 -              if (!options->file)
 -                      die_errno("Could not open '%s'", path);
 +              options->file = xfopen(path, "w");
                options->close_file = 1;
                if (options->use_color != GIT_COLOR_ALWAYS)
                        options->use_color = GIT_COLOR_NEVER;
@@@ -4582,7 -4584,7 +4582,7 @@@ static void patch_id_add_mode(git_SHA_C
  }
  
  /* returns 0 upon success, and writes result into sha1 */
- static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
+ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
  {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
                if (DIFF_PAIR_UNMERGED(p))
                        continue;
  
-               diff_fill_sha1_info(p->one);
-               diff_fill_sha1_info(p->two);
+               diff_fill_oid_info(p->one);
+               diff_fill_oid_info(p->two);
  
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
                if (diff_filespec_is_binary(p->one) ||
                    diff_filespec_is_binary(p->two)) {
                        git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
-                                       40);
+                                       GIT_SHA1_HEXSZ);
                        git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
-                                       40);
+                                       GIT_SHA1_HEXSZ);
                        continue;
                }
  
                                     p->one->path);
        }
  
-       git_SHA1_Final(sha1, &ctx);
+       git_SHA1_Final(oid->hash, &ctx);
        return 0;
  }
  
- int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
+ int diff_flush_patch_id(struct diff_options *options, struct object_id *oid, int diff_header_only)
  {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
-       int result = diff_get_patch_id(options, sha1, diff_header_only);
+       int result = diff_get_patch_id(options, oid, diff_header_only);
  
        for (i = 0; i < q->nr; i++)
                diff_free_filepair(q->queue[i]);
@@@ -4805,7 -4807,9 +4805,7 @@@ void diff_flush(struct diff_options *op
                 */
                if (options->close_file)
                        fclose(options->file);
 -              options->file = fopen("/dev/null", "w");
 -              if (!options->file)
 -                      die_errno("Could not open /dev/null");
 +              options->file = xfopen("/dev/null", "w");
                options->close_file = 1;
                for (i = 0; i < q->nr; i++) {
                        struct diff_filepair *p = q->queue[i];
@@@ -5077,8 -5081,8 +5077,8 @@@ static int is_submodule_ignored(const c
  
  void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
-                   const unsigned char *sha1,
-                   int sha1_valid,
+                   const struct object_id *oid,
+                   int oid_valid,
                    const char *concatpath, unsigned dirty_submodule)
  {
        struct diff_filespec *one, *two;
        two = alloc_filespec(concatpath);
  
        if (addremove != '+')
-               fill_filespec(one, sha1, sha1_valid, mode);
+               fill_filespec(one, oid, oid_valid, mode);
        if (addremove != '-') {
-               fill_filespec(two, sha1, sha1_valid, mode);
+               fill_filespec(two, oid, oid_valid, mode);
                two->dirty_submodule = dirty_submodule;
        }
  
  
  void diff_change(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
-                const unsigned char *old_sha1,
-                const unsigned char *new_sha1,
-                int old_sha1_valid, int new_sha1_valid,
+                const struct object_id *old_oid,
+                const struct object_id *new_oid,
+                int old_oid_valid, int new_oid_valid,
                 const char *concatpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
  
        if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
                SWAP(old_mode, new_mode);
-               SWAP(old_sha1, new_sha1);
-               SWAP(old_sha1_valid, new_sha1_valid);
+               SWAP(old_oid, new_oid);
+               SWAP(old_oid_valid, new_oid_valid);
                SWAP(old_dirty_submodule, new_dirty_submodule);
        }
  
  
        one = alloc_filespec(concatpath);
        two = alloc_filespec(concatpath);
-       fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
-       fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
+       fill_filespec(one, old_oid, old_oid_valid, old_mode);
+       fill_filespec(two, new_oid, new_oid_valid, new_mode);
        one->dirty_submodule = old_dirty_submodule;
        two->dirty_submodule = new_dirty_submodule;
        p = diff_queue(&diff_queued_diff, one, two);
@@@ -5266,29 -5270,6 +5266,29 @@@ size_t fill_textconv(struct userdiff_dr
        return size;
  }
  
-       fill_filespec(df, oid->hash, oid_valid, mode);
 +int textconv_object(const char *path,
 +                  unsigned mode,
 +                  const struct object_id *oid,
 +                  int oid_valid,
 +                  char **buf,
 +                  unsigned long *buf_size)
 +{
 +      struct diff_filespec *df;
 +      struct userdiff_driver *textconv;
 +
 +      df = alloc_filespec(path);
++      fill_filespec(df, oid, oid_valid, mode);
 +      textconv = get_textconv(df);
 +      if (!textconv) {
 +              free_filespec(df);
 +              return 0;
 +      }
 +
 +      *buf_size = fill_textconv(textconv, df, buf);
 +      free_filespec(df);
 +      return 1;
 +}
 +
  void setup_diff_pager(struct diff_options *opt)
  {
        /*
diff --combined diff.h
index 67537f17ed4ae1e64a60a4bdaaa97625cb3ffd15,0d0c14f285dc94d8727a5851f57ace8819453762..2d442e296f9821ea4085188602b9ea4a4ba159a6
--- 1/diff.h
--- 2/diff.h
+++ b/diff.h
@@@ -23,16 -23,16 +23,16 @@@ typedef int (*pathchange_fn_t)(struct d
  
  typedef void (*change_fn_t)(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
-                const unsigned char *old_sha1,
-                const unsigned char *new_sha1,
-                int old_sha1_valid, int new_sha1_valid,
+                const struct object_id *old_oid,
+                const struct object_id *new_oid,
+                int old_oid_valid, int new_oid_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule);
  
  typedef void (*add_remove_fn_t)(struct diff_options *options,
                    int addremove, unsigned mode,
-                   const unsigned char *sha1,
-                   int sha1_valid,
+                   const struct object_id *oid,
+                   int oid_valid,
                    const char *fullpath, unsigned dirty_submodule);
  
  typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
@@@ -210,13 -210,14 +210,14 @@@ const char *diff_line_prefix(struct dif
  extern const char mime_boundary_leader[];
  
  extern struct combine_diff_path *diff_tree_paths(
-       struct combine_diff_path *p, const unsigned char *sha1,
-       const unsigned char **parent_sha1, int nparent,
+       struct combine_diff_path *p, const struct object_id *oid,
+       const struct object_id **parents_oid, int nparent,
        struct strbuf *base, struct diff_options *opt);
- extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
-                         const char *base, struct diff_options *opt);
- extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
-                                struct diff_options *opt);
+ extern int diff_tree_oid(const struct object_id *old_oid,
+                        const struct object_id *new_oid,
+                        const char *base, struct diff_options *opt);
+ extern int diff_root_tree_oid(const struct object_id *new_oid, const char *base,
+                             struct diff_options *opt);
  
  struct combine_diff_path {
        struct combine_diff_path *next;
  extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
                              int dense, struct rev_info *);
  
- extern void diff_tree_combined(const unsigned char *sha1, const struct oid_array *parents, int dense, struct rev_info *rev);
+ extern void diff_tree_combined(const struct object_id *oid, const struct oid_array *parents, int dense, struct rev_info *rev);
  
  extern void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev);
  
@@@ -247,16 -248,15 +248,15 @@@ extern int diff_can_quit_early(struct d
  extern void diff_addremove(struct diff_options *,
                           int addremove,
                           unsigned mode,
-                          const unsigned char *sha1,
-                          int sha1_valid,
+                          const struct object_id *oid,
+                          int oid_valid,
                           const char *fullpath, unsigned dirty_submodule);
  
  extern void diff_change(struct diff_options *,
                        unsigned mode1, unsigned mode2,
-                       const unsigned char *sha1,
-                       const unsigned char *sha2,
-                       int sha1_valid,
-                       int sha2_valid,
+                       const struct object_id *old_oid,
+                       const struct object_id *new_oid,
+                       int old_oid_valid, int new_oid_valid,
                        const char *fullpath,
                        unsigned dirty_submodule1, unsigned dirty_submodule2);
  
@@@ -355,7 -355,7 +355,7 @@@ extern int run_diff_files(struct rev_in
  extern int run_diff_index(struct rev_info *revs, int cached);
  
  extern int do_diff_cache(const struct object_id *, struct diff_options *);
- extern int diff_flush_patch_id(struct diff_options *, unsigned char *, int);
+ extern int diff_flush_patch_id(struct diff_options *, struct object_id *, int);
  
  extern int diff_result_code(struct diff_options *, int);
  
@@@ -385,13 -385,6 +385,13 @@@ extern size_t fill_textconv(struct user
   */
  extern struct userdiff_driver *get_textconv(struct diff_filespec *one);
  
 +/*
 + * Prepare diff_filespec and convert it using diff textconv API
 + * if the textconv driver exists.
 + * Return 1 if the conversion succeeds, 0 otherwise.
 + */
 +extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);
 +
  extern int parse_rename_score(const char **cp_p);
  
  extern long parse_algorithm_value(const char *value);
diff --combined grep.c
index d0bf37858a9011569d68f8cb88656a67a7a1738a,3e21c92b9d49a2b68062158c47204f6dfdaa1c64..d7ef21358ea7f592dffff98884eb34169e097880
--- 1/grep.c
--- 2/grep.c
+++ b/grep.c
@@@ -178,38 -178,26 +178,38 @@@ static void grep_set_pattern_type_optio
  
        case GREP_PATTERN_TYPE_BRE:
                opt->fixed = 0;
 -              opt->pcre = 0;
 -              opt->regflags &= ~REG_EXTENDED;
 +              opt->pcre1 = 0;
 +              opt->pcre2 = 0;
                break;
  
        case GREP_PATTERN_TYPE_ERE:
                opt->fixed = 0;
 -              opt->pcre = 0;
 +              opt->pcre1 = 0;
 +              opt->pcre2 = 0;
                opt->regflags |= REG_EXTENDED;
                break;
  
        case GREP_PATTERN_TYPE_FIXED:
                opt->fixed = 1;
 -              opt->pcre = 0;
 -              opt->regflags &= ~REG_EXTENDED;
 +              opt->pcre1 = 0;
 +              opt->pcre2 = 0;
                break;
  
        case GREP_PATTERN_TYPE_PCRE:
                opt->fixed = 0;
 -              opt->pcre = 1;
 -              opt->regflags &= ~REG_EXTENDED;
 +#ifdef USE_LIBPCRE2
 +              opt->pcre1 = 0;
 +              opt->pcre2 = 1;
 +#else
 +              /*
 +               * It's important that pcre1 always be assigned to
 +               * even when there's no USE_LIBPCRE* defined. We still
 +               * call the PCRE stub function, it just dies with
 +               * "cannot use Perl-compatible regexes[...]".
 +               */
 +              opt->pcre1 = 1;
 +              opt->pcre2 = 0;
 +#endif
                break;
        }
  }
@@@ -336,32 -324,8 +336,32 @@@ static NORETURN void compile_regexp_fai
        die("%s'%s': %s", where, p->pattern, error);
  }
  
 -#ifdef USE_LIBPCRE
 -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
 +static int is_fixed(const char *s, size_t len)
 +{
 +      size_t i;
 +
 +      for (i = 0; i < len; i++) {
 +              if (is_regex_special(s[i]))
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +static int has_null(const char *s, size_t len)
 +{
 +      /*
 +       * regcomp cannot accept patterns with NULs so when using it
 +       * we consider any pattern containing a NUL fixed.
 +       */
 +      if (memchr(s, 0, len))
 +              return 1;
 +
 +      return 0;
 +}
 +
 +#ifdef USE_LIBPCRE1
 +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
  {
        const char *error;
        int erroffset;
  
        if (opt->ignore_case) {
                if (has_non_ascii(p->pattern))
 -                      p->pcre_tables = pcre_maketables();
 +                      p->pcre1_tables = pcre_maketables();
                options |= PCRE_CASELESS;
        }
        if (is_utf8_locale() && has_non_ascii(p->pattern))
                options |= PCRE_UTF8;
  
 -      p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
 -                                    p->pcre_tables);
 -      if (!p->pcre_regexp)
 +      p->pcre1_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
 +                                    p->pcre1_tables);
 +      if (!p->pcre1_regexp)
                compile_regexp_failed(p, error);
  
 -      p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error);
 -      if (!p->pcre_extra_info && error)
 +      p->pcre1_extra_info = pcre_study(p->pcre1_regexp, PCRE_STUDY_JIT_COMPILE, &error);
 +      if (!p->pcre1_extra_info && error)
                die("%s", error);
 +
 +#ifdef GIT_PCRE1_USE_JIT
 +      pcre_config(PCRE_CONFIG_JIT, &p->pcre1_jit_on);
 +      if (p->pcre1_jit_on == 1) {
 +              p->pcre1_jit_stack = pcre_jit_stack_alloc(1, 1024 * 1024);
 +              if (!p->pcre1_jit_stack)
 +                      die("Couldn't allocate PCRE JIT stack");
 +              pcre_assign_jit_stack(p->pcre1_extra_info, NULL, p->pcre1_jit_stack);
 +      } else if (p->pcre1_jit_on != 0) {
 +              die("BUG: The pcre1_jit_on variable should be 0 or 1, not %d",
 +                  p->pcre1_jit_on);
 +      }
 +#endif
  }
  
 -static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
 +static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
                regmatch_t *match, int eflags)
  {
        int ovector[30], ret, flags = 0;
        if (eflags & REG_NOTBOL)
                flags |= PCRE_NOTBOL;
  
 -      ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line,
 -                      0, flags, ovector, ARRAY_SIZE(ovector));
 +#ifdef GIT_PCRE1_USE_JIT
 +      if (p->pcre1_jit_on) {
 +              ret = pcre_jit_exec(p->pcre1_regexp, p->pcre1_extra_info, line,
 +                                  eol - line, 0, flags, ovector,
 +                                  ARRAY_SIZE(ovector), p->pcre1_jit_stack);
 +      } else
 +#endif
 +      {
 +              ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line,
 +                              eol - line, 0, flags, ovector,
 +                              ARRAY_SIZE(ovector));
 +      }
 +
        if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
                die("pcre_exec failed with error code %d", ret);
        if (ret > 0) {
        return ret;
  }
  
 -static void free_pcre_regexp(struct grep_pat *p)
 +static void free_pcre1_regexp(struct grep_pat *p)
  {
 -      pcre_free(p->pcre_regexp);
 -      pcre_free(p->pcre_extra_info);
 -      pcre_free((void *)p->pcre_tables);
 +      pcre_free(p->pcre1_regexp);
 +#ifdef GIT_PCRE1_USE_JIT
 +      if (p->pcre1_jit_on) {
 +              pcre_free_study(p->pcre1_extra_info);
 +              pcre_jit_stack_free(p->pcre1_jit_stack);
 +      } else
 +#endif
 +      {
 +              pcre_free(p->pcre1_extra_info);
 +      }
 +      pcre_free((void *)p->pcre1_tables);
  }
 -#else /* !USE_LIBPCRE */
 -static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
 +#else /* !USE_LIBPCRE1 */
 +static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
  {
        die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
  }
  
 -static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
 +static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
                regmatch_t *match, int eflags)
  {
        return 1;
  }
  
 -static void free_pcre_regexp(struct grep_pat *p)
 +static void free_pcre1_regexp(struct grep_pat *p)
  {
  }
 -#endif /* !USE_LIBPCRE */
 +#endif /* !USE_LIBPCRE1 */
  
 -static int is_fixed(const char *s, size_t len)
 +#ifdef USE_LIBPCRE2
 +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
  {
 -      size_t i;
 +      int error;
 +      PCRE2_UCHAR errbuf[256];
 +      PCRE2_SIZE erroffset;
 +      int options = PCRE2_MULTILINE;
 +      const uint8_t *character_tables = NULL;
 +      int jitret;
  
 -      /* regcomp cannot accept patterns with NULs so we
 -       * consider any pattern containing a NUL fixed.
 -       */
 -      if (memchr(s, 0, len))
 -              return 1;
 +      assert(opt->pcre2);
  
 -      for (i = 0; i < len; i++) {
 -              if (is_regex_special(s[i]))
 -                      return 0;
 +      p->pcre2_compile_context = NULL;
 +
 +      if (opt->ignore_case) {
 +              if (has_non_ascii(p->pattern)) {
 +                      character_tables = pcre2_maketables(NULL);
 +                      p->pcre2_compile_context = pcre2_compile_context_create(NULL);
 +                      pcre2_set_character_tables(p->pcre2_compile_context, character_tables);
 +              }
 +              options |= PCRE2_CASELESS;
 +      }
 +      if (is_utf8_locale() && has_non_ascii(p->pattern))
 +              options |= PCRE2_UTF;
 +
 +      p->pcre2_pattern = pcre2_compile((PCRE2_SPTR)p->pattern,
 +                                       p->patternlen, options, &error, &erroffset,
 +                                       p->pcre2_compile_context);
 +
 +      if (p->pcre2_pattern) {
 +              p->pcre2_match_data = pcre2_match_data_create_from_pattern(p->pcre2_pattern, NULL);
 +              if (!p->pcre2_match_data)
 +                      die("Couldn't allocate PCRE2 match data");
 +      } else {
 +              pcre2_get_error_message(error, errbuf, sizeof(errbuf));
 +              compile_regexp_failed(p, (const char *)&errbuf);
 +      }
 +
 +      pcre2_config(PCRE2_CONFIG_JIT, &p->pcre2_jit_on);
 +      if (p->pcre2_jit_on == 1) {
 +              jitret = pcre2_jit_compile(p->pcre2_pattern, PCRE2_JIT_COMPLETE);
 +              if (jitret)
 +                      die("Couldn't JIT the PCRE2 pattern '%s', got '%d'\n", p->pattern, jitret);
 +              p->pcre2_jit_stack = pcre2_jit_stack_create(1, 1024 * 1024, NULL);
 +              if (!p->pcre2_jit_stack)
 +                      die("Couldn't allocate PCRE2 JIT stack");
 +              p->pcre2_match_context = pcre2_match_context_create(NULL);
 +              if (!p->pcre2_jit_stack)
 +                      die("Couldn't allocate PCRE2 match context");
 +              pcre2_jit_stack_assign(p->pcre2_match_context, NULL, p->pcre2_jit_stack);
 +      } else if (p->pcre2_jit_on != 0) {
 +              die("BUG: The pcre2_jit_on variable should be 0 or 1, not %d",
 +                  p->pcre1_jit_on);
 +      }
 +}
 +
 +static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
 +              regmatch_t *match, int eflags)
 +{
 +      int ret, flags = 0;
 +      PCRE2_SIZE *ovector;
 +      PCRE2_UCHAR errbuf[256];
 +
 +      if (eflags & REG_NOTBOL)
 +              flags |= PCRE2_NOTBOL;
 +
 +      if (p->pcre2_jit_on)
 +              ret = pcre2_jit_match(p->pcre2_pattern, (unsigned char *)line,
 +                                    eol - line, 0, flags, p->pcre2_match_data,
 +                                    NULL);
 +      else
 +              ret = pcre2_match(p->pcre2_pattern, (unsigned char *)line,
 +                                eol - line, 0, flags, p->pcre2_match_data,
 +                                NULL);
 +
 +      if (ret < 0 && ret != PCRE2_ERROR_NOMATCH) {
 +              pcre2_get_error_message(ret, errbuf, sizeof(errbuf));
 +              die("%s failed with error code %d: %s",
 +                  (p->pcre2_jit_on ? "pcre2_jit_match" : "pcre2_match"), ret,
 +                  errbuf);
 +      }
 +      if (ret > 0) {
 +              ovector = pcre2_get_ovector_pointer(p->pcre2_match_data);
 +              ret = 0;
 +              match->rm_so = (int)ovector[0];
 +              match->rm_eo = (int)ovector[1];
        }
  
 +      return ret;
 +}
 +
 +static void free_pcre2_pattern(struct grep_pat *p)
 +{
 +      pcre2_compile_context_free(p->pcre2_compile_context);
 +      pcre2_code_free(p->pcre2_pattern);
 +      pcre2_match_data_free(p->pcre2_match_data);
 +      pcre2_jit_stack_free(p->pcre2_jit_stack);
 +      pcre2_match_context_free(p->pcre2_match_context);
 +}
 +#else /* !USE_LIBPCRE2 */
 +static void compile_pcre2_pattern(struct grep_pat *p, const struct grep_opt *opt)
 +{
 +      /*
 +       * Unreachable until USE_LIBPCRE2 becomes synonymous with
 +       * USE_LIBPCRE. See the sibling comment in
 +       * grep_set_pattern_type_option().
 +       */
 +      die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
 +}
 +
 +static int pcre2match(struct grep_pat *p, const char *line, const char *eol,
 +              regmatch_t *match, int eflags)
 +{
        return 1;
  }
  
 +static void free_pcre2_pattern(struct grep_pat *p)
 +{
 +}
 +#endif /* !USE_LIBPCRE2 */
 +
  static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt)
  {
        struct strbuf sb = STRBUF_INIT;
        int err;
 -      int regflags;
 +      int regflags = opt->regflags;
  
        basic_regex_quote_buf(&sb, p->pattern);
 -      regflags = opt->regflags & ~REG_EXTENDED;
        if (opt->ignore_case)
                regflags |= REG_ICASE;
        err = regcomp(&p->regexp, sb.buf, regflags);
@@@ -625,9 -455,7 +625,9 @@@ static void compile_regexp(struct grep_
         * simple string match using kws.  p->fixed tells us if we
         * want to use kws.
         */
 -      if (opt->fixed || is_fixed(p->pattern, p->patternlen))
 +      if (opt->fixed ||
 +          has_null(p->pattern, p->patternlen) ||
 +          is_fixed(p->pattern, p->patternlen))
                p->fixed = !icase || ascii_only;
        else
                p->fixed = 0;
                return;
        }
  
 -      if (opt->pcre) {
 -              compile_pcre_regexp(p, opt);
 +      if (opt->pcre2) {
 +              compile_pcre2_pattern(p, opt);
 +              return;
 +      }
 +
 +      if (opt->pcre1) {
 +              compile_pcre1_regexp(p, opt);
                return;
        }
  
@@@ -1009,10 -832,8 +1009,10 @@@ void free_grep_patterns(struct grep_op
                case GREP_PATTERN_BODY:
                        if (p->kws)
                                kwsfree(p->kws);
 -                      else if (p->pcre_regexp)
 -                              free_pcre_regexp(p);
 +                      else if (p->pcre1_regexp)
 +                              free_pcre1_regexp(p);
 +                      else if (p->pcre2_pattern)
 +                              free_pcre2_pattern(p);
                        else
                                regfree(&p->regexp);
                        free(p->pattern);
@@@ -1091,10 -912,8 +1091,10 @@@ static int patmatch(struct grep_pat *p
  
        if (p->fixed)
                hit = !fixmatch(p, line, eol, match);
 -      else if (p->pcre_regexp)
 -              hit = !pcrematch(p, line, eol, match, eflags);
 +      else if (p->pcre1_regexp)
 +              hit = !pcre1match(p, line, eol, match, eflags);
 +      else if (p->pcre2_pattern)
 +              hit = !pcre2match(p, line, eol, match, eflags);
        else
                hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
                                   eflags);
@@@ -1584,11 -1403,11 +1584,11 @@@ static int fill_textconv_grep(struct us
         */
        df = alloc_filespec(gs->path);
        switch (gs->type) {
-       case GREP_SOURCE_SHA1:
+       case GREP_SOURCE_OID:
                fill_filespec(df, gs->identifier, 1, 0100644);
                break;
        case GREP_SOURCE_FILE:
-               fill_filespec(df, null_sha1, 0, 0100644);
+               fill_filespec(df, &null_oid, 0, 0100644);
                break;
        default:
                die("BUG: attempt to textconv something without a path?");
@@@ -1928,9 -1747,8 +1928,8 @@@ void grep_source_init(struct grep_sourc
                 * If the identifier is non-NULL (in the submodule case) it
                 * will be a SHA1 that needs to be copied.
                 */
-       case GREP_SOURCE_SHA1:
-               gs->identifier = xmalloc(20);
-               hashcpy(gs->identifier, identifier);
+       case GREP_SOURCE_OID:
+               gs->identifier = oiddup(identifier);
                break;
        case GREP_SOURCE_BUF:
                gs->identifier = NULL;
@@@ -1953,7 -1771,7 +1952,7 @@@ void grep_source_clear_data(struct grep
  {
        switch (gs->type) {
        case GREP_SOURCE_FILE:
-       case GREP_SOURCE_SHA1:
+       case GREP_SOURCE_OID:
        case GREP_SOURCE_SUBMODULE:
                free(gs->buf);
                gs->buf = NULL;
        }
  }
  
- static int grep_source_load_sha1(struct grep_source *gs)
+ static int grep_source_load_oid(struct grep_source *gs)
  {
        enum object_type type;
  
        if (!gs->buf)
                return error(_("'%s': unable to read %s"),
                             gs->name,
-                            sha1_to_hex(gs->identifier));
+                            oid_to_hex(gs->identifier));
        return 0;
  }
  
@@@ -2022,8 -1840,8 +2021,8 @@@ static int grep_source_load(struct grep
        switch (gs->type) {
        case GREP_SOURCE_FILE:
                return grep_source_load_file(gs);
-       case GREP_SOURCE_SHA1:
-               return grep_source_load_sha1(gs);
+       case GREP_SOURCE_OID:
+               return grep_source_load_oid(gs);
        case GREP_SOURCE_BUF:
                return gs->buf ? 0 : -1;
        case GREP_SOURCE_SUBMODULE:
diff --combined grep.h
index 6f3d4e19548016b5eee5a9d02399b67e57536cdf,c88b40bdc41435677c1ccafcdd5263697e63647b..b8f93bfc2d518beddc90fed74e419cb1c6fb07f1
--- 1/grep.h
--- 2/grep.h
+++ b/grep.h
@@@ -1,35 -1,11 +1,35 @@@
  #ifndef GREP_H
  #define GREP_H
  #include "color.h"
 -#ifdef USE_LIBPCRE
 +#ifdef USE_LIBPCRE1
  #include <pcre.h>
 +#ifdef PCRE_CONFIG_JIT
 +#if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32
 +#ifndef NO_LIBPCRE1_JIT
 +#define GIT_PCRE1_USE_JIT
 +#endif
 +#endif
 +#endif
 +#ifndef PCRE_STUDY_JIT_COMPILE
 +#define PCRE_STUDY_JIT_COMPILE 0
 +#endif
 +#if PCRE_MAJOR <= 8 && PCRE_MINOR < 20
 +typedef int pcre_jit_stack;
 +#endif
  #else
  typedef int pcre;
  typedef int pcre_extra;
 +typedef int pcre_jit_stack;
 +#endif
 +#ifdef USE_LIBPCRE2
 +#define PCRE2_CODE_UNIT_WIDTH 8
 +#include <pcre2.h>
 +#else
 +typedef int pcre2_code;
 +typedef int pcre2_match_data;
 +typedef int pcre2_compile_context;
 +typedef int pcre2_match_context;
 +typedef int pcre2_jit_stack;
  #endif
  #include "kwset.h"
  #include "thread-utils.h"
@@@ -70,17 -46,9 +70,17 @@@ struct grep_pat 
        size_t patternlen;
        enum grep_header_field field;
        regex_t regexp;
 -      pcre *pcre_regexp;
 -      pcre_extra *pcre_extra_info;
 -      const unsigned char *pcre_tables;
 +      pcre *pcre1_regexp;
 +      pcre_extra *pcre1_extra_info;
 +      pcre_jit_stack *pcre1_jit_stack;
 +      const unsigned char *pcre1_tables;
 +      int pcre1_jit_on;
 +      pcre2_code *pcre2_pattern;
 +      pcre2_match_data *pcre2_match_data;
 +      pcre2_compile_context *pcre2_compile_context;
 +      pcre2_match_context *pcre2_match_context;
 +      pcre2_jit_stack *pcre2_jit_stack;
 +      uint32_t pcre2_jit_on;
        kwset_t kws;
        unsigned fixed:1;
        unsigned ignore_case:1;
@@@ -143,8 -111,7 +143,8 @@@ struct grep_opt 
        int allow_textconv;
        int extended;
        int use_reflog_filter;
 -      int pcre;
 +      int pcre1;
 +      int pcre2;
        int relative;
        int pathname;
        int null_following_name;
@@@ -191,7 -158,7 +191,7 @@@ struct grep_source 
        char *name;
  
        enum grep_source_type {
-               GREP_SOURCE_SHA1,
+               GREP_SOURCE_OID,
                GREP_SOURCE_FILE,
                GREP_SOURCE_BUF,
                GREP_SOURCE_SUBMODULE,
diff --combined notes-utils.c
index 031503d7b2ba9217cffa52652b57aa063208fb1a,b2aada90a2eab97aa31b57da356a738636c32b6c..9ebf8419565019cca42d778be96e2f3b7d6f29df
@@@ -132,11 -132,8 +132,11 @@@ struct notes_rewrite_cfg *init_copy_not
                c->mode_from_env = 1;
                c->combine = parse_combine_notes_fn(rewrite_mode_env);
                if (!c->combine)
 -                      /* TRANSLATORS: The first %s is the name of the
 -                         environment variable, the second %s is its value */
 +                      /*
 +                       * TRANSLATORS: The first %s is the name of
 +                       * the environment variable, the second %s is
 +                       * its value.
 +                       */
                        error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
                                        rewrite_mode_env);
        }
  }
  
  int copy_note_for_rewrite(struct notes_rewrite_cfg *c,
-                         const unsigned char *from_obj, const unsigned char *to_obj)
+                         const struct object_id *from_obj, const struct object_id *to_obj)
  {
        int ret = 0;
        int i;
diff --combined remote-testsvn.c
index 50404ef3438f37ae33e3590f1e7205581087f75a,8e8d5c7947f815e143c1fd16ab4c0a23e17a02a3..e034ea00d49c81675d664085c0d67763609bfd8a
@@@ -51,17 -51,17 +51,17 @@@ static void terminate_batch(void
  }
  
  /* NOTE: 'ref' refers to a git reference, while 'rev' refers to a svn revision. */
- static char *read_ref_note(const unsigned char sha1[20])
+ static char *read_ref_note(const struct object_id *oid)
  {
-       const unsigned char *note_sha1;
+       const struct object_id *note_oid;
        char *msg = NULL;
        unsigned long msglen;
        enum object_type type;
  
        init_notes(NULL, notes_ref, NULL, 0);
-       if (!(note_sha1 = get_note(NULL, sha1)))
+       if (!(note_oid = get_note(NULL, oid)))
                return NULL;    /* note tree not found */
-       if (!(msg = read_sha1_file(note_sha1, &type, &msglen)))
+       if (!(msg = read_sha1_file(note_oid->hash, &type, &msglen)))
                error("Empty notes tree. %s", notes_ref);
        else if (!msglen || type != OBJ_BLOB) {
                error("Note contains unusable content. "
@@@ -99,8 -99,8 +99,8 @@@ static int parse_rev_note(const char *m
        return -1;
  }
  
- static int note2mark_cb(const unsigned char *object_sha1,
-               const unsigned char *note_sha1, char *note_path,
+ static int note2mark_cb(const struct object_id *object_oid,
+               const struct object_id *note_oid, char *note_path,
                void *cb_data)
  {
        FILE *file = (FILE *)cb_data;
        enum object_type type;
        struct rev_note note;
  
-       if (!(msg = read_sha1_file(note_sha1, &type, &msglen)) ||
+       if (!(msg = read_sha1_file(note_oid->hash, &type, &msglen)) ||
                        !msglen || type != OBJ_BLOB) {
                free(msg);
                return 1;
        }
        if (parse_rev_note(msg, &note))
                return 2;
-       if (fprintf(file, ":%d %s\n", note.rev_nr, sha1_to_hex(object_sha1)) < 1)
+       if (fprintf(file, ":%d %s\n", note.rev_nr, oid_to_hex(object_oid)) < 1)
                return 3;
        return 0;
  }
  static void regenerate_marks(void)
  {
        int ret;
 -      FILE *marksfile = fopen(marksfilename, "w+");
 +      FILE *marksfile = xfopen(marksfilename, "w+");
  
 -      if (!marksfile)
 -              die_errno("Couldn't create mark file %s.", marksfilename);
        ret = for_each_note(NULL, 0, note2mark_cb, marksfile);
        if (ret)
                die("Regeneration of marks failed, returned %d.", ret);
@@@ -146,7 -148,9 +146,7 @@@ static void check_or_regenerate_marks(i
        marksfile = fopen(marksfilename, "r");
        if (!marksfile) {
                regenerate_marks();
 -              marksfile = fopen(marksfilename, "r");
 -              if (!marksfile)
 -                      die_errno("cannot read marks file %s!", marksfilename);
 +              marksfile = xfopen(marksfilename, "r");
                fclose(marksfile);
        } else {
                strbuf_addf(&sb, ":%d ", latestrev);
@@@ -170,15 -174,15 +170,15 @@@ static int cmd_import(const char *line
        int code;
        int dumpin_fd;
        char *note_msg;
-       unsigned char head_sha1[20];
+       struct object_id head_oid;
        unsigned int startrev;
        struct child_process svndump_proc = CHILD_PROCESS_INIT;
        const char *command = "svnrdump";
  
-       if (read_ref(private_ref, head_sha1))
+       if (read_ref(private_ref, head_oid.hash))
                startrev = 0;
        else {
-               note_msg = read_ref_note(head_sha1);
+               note_msg = read_ref_note(&head_oid);
                if(note_msg == NULL) {
                        warning("No note found for %s.", private_ref);
                        startrev = 0;
diff --combined revision.c
index 3b09cd6ee9fcc56883ab2991fd584f4fc763df27,3030f33eedb6381d03607059edbb3bba1da6073c..12eb332350e2dd96bc1c6fee0d4f62f2556569a1
@@@ -401,8 -401,8 +401,8 @@@ static int tree_difference = REV_TREE_S
  
  static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
-                   const unsigned char *sha1,
-                   int sha1_valid,
+                   const struct object_id *oid,
+                   int oid_valid,
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
  
  static void file_change(struct diff_options *options,
                 unsigned old_mode, unsigned new_mode,
-                const unsigned char *old_sha1,
-                const unsigned char *new_sha1,
-                int old_sha1_valid, int new_sha1_valid,
+                const struct object_id *old_oid,
+                const struct object_id *new_oid,
+                int old_oid_valid, int new_oid_valid,
                 const char *fullpath,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
@@@ -455,7 -455,7 +455,7 @@@ static int rev_compare_tree(struct rev_
  
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
-       if (diff_tree_sha1(t1->object.oid.hash, t2->object.oid.hash, "",
+       if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
        return tree_difference;
@@@ -471,7 -471,7 +471,7 @@@ static int rev_same_tree_as_empty(struc
  
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
-       retval = diff_tree_sha1(NULL, t1->object.oid.hash, "", &revs->pruning);
+       retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
  
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
  }
@@@ -1429,168 -1429,134 +1429,168 @@@ static void prepare_show_merge(struct r
        revs->limited = 1;
  }
  
 +static int dotdot_missing(const char *arg, char *dotdot,
 +                        struct rev_info *revs, int symmetric)
 +{
 +      if (revs->ignore_missing)
 +              return 0;
 +      /* de-munge so we report the full argument */
 +      *dotdot = '.';
 +      die(symmetric
 +          ? "Invalid symmetric difference expression %s"
 +          : "Invalid revision range %s", arg);
 +}
 +
 +static int handle_dotdot_1(const char *arg, char *dotdot,
 +                         struct rev_info *revs, int flags,
 +                         int cant_be_filename,
 +                         struct object_context *a_oc,
 +                         struct object_context *b_oc)
 +{
 +      const char *a_name, *b_name;
 +      struct object_id a_oid, b_oid;
 +      struct object *a_obj, *b_obj;
 +      unsigned int a_flags, b_flags;
 +      int symmetric = 0;
 +      unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
 +      unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH;
 +
 +      a_name = arg;
 +      if (!*a_name)
 +              a_name = "HEAD";
 +
 +      b_name = dotdot + 2;
 +      if (*b_name == '.') {
 +              symmetric = 1;
 +              b_name++;
 +      }
 +      if (!*b_name)
 +              b_name = "HEAD";
 +
 +      if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) ||
 +          get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc))
 +              return -1;
 +
 +      if (!cant_be_filename) {
 +              *dotdot = '.';
 +              verify_non_filename(revs->prefix, arg);
 +              *dotdot = '\0';
 +      }
 +
 +      a_obj = parse_object(&a_oid);
 +      b_obj = parse_object(&b_oid);
 +      if (!a_obj || !b_obj)
 +              return dotdot_missing(arg, dotdot, revs, symmetric);
 +
 +      if (!symmetric) {
 +              /* just A..B */
 +              b_flags = flags;
 +              a_flags = flags_exclude;
 +      } else {
 +              /* A...B -- find merge bases between the two */
 +              struct commit *a, *b;
 +              struct commit_list *exclude;
 +
 +              a = lookup_commit_reference(&a_obj->oid);
 +              b = lookup_commit_reference(&b_obj->oid);
 +              if (!a || !b)
 +                      return dotdot_missing(arg, dotdot, revs, symmetric);
 +
 +              exclude = get_merge_bases(a, b);
 +              add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
 +                                   flags_exclude);
 +              add_pending_commit_list(revs, exclude, flags_exclude);
 +              free_commit_list(exclude);
 +
 +              b_flags = flags;
 +              a_flags = flags | SYMMETRIC_LEFT;
 +      }
 +
 +      a_obj->flags |= a_flags;
 +      b_obj->flags |= b_flags;
 +      add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags);
 +      add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags);
 +      add_pending_object_with_path(revs, a_obj, a_name, a_oc->mode, a_oc->path);
 +      add_pending_object_with_path(revs, b_obj, b_name, b_oc->mode, b_oc->path);
 +      return 0;
 +}
 +
 +static int handle_dotdot(const char *arg,
 +                       struct rev_info *revs, int flags,
 +                       int cant_be_filename)
 +{
 +      struct object_context a_oc, b_oc;
 +      char *dotdot = strstr(arg, "..");
 +      int ret;
 +
 +      if (!dotdot)
 +              return -1;
 +
 +      memset(&a_oc, 0, sizeof(a_oc));
 +      memset(&b_oc, 0, sizeof(b_oc));
 +
 +      *dotdot = '\0';
 +      ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
 +                            &a_oc, &b_oc);
 +      *dotdot = '.';
 +
 +      free(a_oc.path);
 +      free(b_oc.path);
 +
 +      return ret;
 +}
 +
  int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
  {
        struct object_context oc;
 -      char *dotdot;
 +      char *mark;
        struct object *object;
        struct object_id oid;
        int local_flags;
        const char *arg = arg_;
        int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
 -      unsigned get_sha1_flags = 0;
 +      unsigned get_sha1_flags = GET_SHA1_RECORD_PATH;
  
        flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
  
 -      dotdot = strstr(arg, "..");
 -      if (dotdot) {
 -              struct object_id from_oid;
 -              const char *next = dotdot + 2;
 -              const char *this = arg;
 -              int symmetric = *next == '.';
 -              unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
 -              static const char head_by_default[] = "HEAD";
 -              unsigned int a_flags;
 -
 -              *dotdot = 0;
 -              next += symmetric;
 -
 -              if (!*next)
 -                      next = head_by_default;
 -              if (dotdot == arg)
 -                      this = head_by_default;
 -              if (this == head_by_default && next == head_by_default &&
 -                  !symmetric) {
 -                      /*
 -                       * Just ".."?  That is not a range but the
 -                       * pathspec for the parent directory.
 -                       */
 -                      if (!cant_be_filename) {
 -                              *dotdot = '.';
 -                              return -1;
 -                      }
 -              }
 -              if (!get_sha1_committish(this, from_oid.hash) &&
 -                  !get_sha1_committish(next, oid.hash)) {
 -                      struct object *a_obj, *b_obj;
 -
 -                      if (!cant_be_filename) {
 -                              *dotdot = '.';
 -                              verify_non_filename(revs->prefix, arg);
 -                      }
 -
 -                      a_obj = parse_object(&from_oid);
 -                      b_obj = parse_object(&oid);
 -                      if (!a_obj || !b_obj) {
 -                      missing:
 -                              if (revs->ignore_missing)
 -                                      return 0;
 -                              die(symmetric
 -                                  ? "Invalid symmetric difference expression %s"
 -                                  : "Invalid revision range %s", arg);
 -                      }
 -
 -                      if (!symmetric) {
 -                              /* just A..B */
 -                              a_flags = flags_exclude;
 -                      } else {
 -                              /* A...B -- find merge bases between the two */
 -                              struct commit *a, *b;
 -                              struct commit_list *exclude;
 -
 -                              a = (a_obj->type == OBJ_COMMIT
 -                                   ? (struct commit *)a_obj
 -                                   : lookup_commit_reference(&a_obj->oid));
 -                              b = (b_obj->type == OBJ_COMMIT
 -                                   ? (struct commit *)b_obj
 -                                   : lookup_commit_reference(&b_obj->oid));
 -                              if (!a || !b)
 -                                      goto missing;
 -                              exclude = get_merge_bases(a, b);
 -                              add_rev_cmdline_list(revs, exclude,
 -                                                   REV_CMD_MERGE_BASE,
 -                                                   flags_exclude);
 -                              add_pending_commit_list(revs, exclude,
 -                                                      flags_exclude);
 -                              free_commit_list(exclude);
 -
 -                              a_flags = flags | SYMMETRIC_LEFT;
 -                      }
 -
 -                      a_obj->flags |= a_flags;
 -                      b_obj->flags |= flags;
 -                      add_rev_cmdline(revs, a_obj, this,
 -                                      REV_CMD_LEFT, a_flags);
 -                      add_rev_cmdline(revs, b_obj, next,
 -                                      REV_CMD_RIGHT, flags);
 -                      add_pending_object(revs, a_obj, this);
 -                      add_pending_object(revs, b_obj, next);
 -                      return 0;
 -              }
 -              *dotdot = '.';
 +      if (!cant_be_filename && !strcmp(arg, "..")) {
 +              /*
 +               * Just ".."?  That is not a range but the
 +               * pathspec for the parent directory.
 +               */
 +              return -1;
        }
  
 -      dotdot = strstr(arg, "^@");
 -      if (dotdot && !dotdot[2]) {
 -              *dotdot = 0;
 +      if (!handle_dotdot(arg, revs, flags, revarg_opt))
 +              return 0;
 +
 +      mark = strstr(arg, "^@");
 +      if (mark && !mark[2]) {
 +              *mark = 0;
                if (add_parents_only(revs, arg, flags, 0))
                        return 0;
 -              *dotdot = '^';
 +              *mark = '^';
        }
 -      dotdot = strstr(arg, "^!");
 -      if (dotdot && !dotdot[2]) {
 -              *dotdot = 0;
 +      mark = strstr(arg, "^!");
 +      if (mark && !mark[2]) {
 +              *mark = 0;
                if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
 -                      *dotdot = '^';
 +                      *mark = '^';
        }
 -      dotdot = strstr(arg, "^-");
 -      if (dotdot) {
 +      mark = strstr(arg, "^-");
 +      if (mark) {
                int exclude_parent = 1;
  
 -              if (dotdot[2]) {
 +              if (mark[2]) {
                        char *end;
 -                      exclude_parent = strtoul(dotdot + 2, &end, 10);
 +                      exclude_parent = strtoul(mark + 2, &end, 10);
                        if (*end != '\0' || !exclude_parent)
                                return -1;
                }
  
 -              *dotdot = 0;
 +              *mark = 0;
                if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
 -                      *dotdot = '^';
 +                      *mark = '^';
        }
  
        local_flags = 0;
        }
  
        if (revarg_opt & REVARG_COMMITTISH)
 -              get_sha1_flags = GET_SHA1_COMMITTISH;
 +              get_sha1_flags |= GET_SHA1_COMMITTISH;
  
        if (get_sha1_with_context(arg, get_sha1_flags, oid.hash, &oc))
                return revs->ignore_missing ? 0 : -1;
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, &oid, flags ^ local_flags);
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
 -      add_pending_object_with_mode(revs, object, arg, oc.mode);
 +      add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
 +      free(oc.path);
        return 0;
  }
  
@@@ -2026,12 -1991,11 +2026,12 @@@ static int handle_revision_opt(struct r
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
 +              revs->grep_filter.ignore_case = 1;
                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.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
 -      } else if (!strcmp(arg, "--perl-regexp")) {
 +      } else if (!strcmp(arg, "--perl-regexp") || !strcmp(arg, "-P")) {
                revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
@@@ -2944,7 -2908,7 +2944,7 @@@ static int commit_match(struct commit *
        if (opt->show_notes) {
                if (!buf.len)
                        strbuf_addstr(&buf, message);
-               format_display_notes(commit->object.oid.hash, &buf, encoding, 1);
+               format_display_notes(&commit->object.oid, &buf, encoding, 1);
        }
  
        /*
diff --combined sequencer.c
index 5282fb849c5c27e98bbe1b506340c906aa37995c,7a114def84e7d8649934d28c51da7306617b089c..d63099d50fc4e23cb3585b12c61ba8863d2688f6
@@@ -464,8 -464,7 +464,8 @@@ static int do_recursive_merge(struct co
  
        if (active_cache_changed &&
            write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
 -              /* TRANSLATORS: %s will be "revert", "cherry-pick" or
 +              /*
 +               * TRANSLATORS: %s will be "revert", "cherry-pick" or
                 * "rebase -i".
                 */
                return error(_("%s: Unable to write new index file"),
@@@ -899,8 -898,8 +899,8 @@@ static void flush_rewritten_pending(voi
        FILE *out;
  
        if (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&
 -                      !get_sha1("HEAD", newsha1) &&
 -                      (out = fopen(rebase_path_rewritten_list(), "a"))) {
 +          !get_sha1("HEAD", newsha1) &&
 +          (out = fopen_or_warn(rebase_path_rewritten_list(), "a"))) {
                char *bol = buf.buf, *eol;
  
                while (*bol) {
  
  static void record_in_rewritten(struct object_id *oid,
                enum todo_command next_command) {
 -      FILE *out = fopen(rebase_path_rewritten_pending(), "a");
 +      FILE *out = fopen_or_warn(rebase_path_rewritten_pending(), "a");
  
        if (!out)
                return;
@@@ -1381,7 -1380,7 +1381,7 @@@ static int read_populate_todo(struct to
  
        if (is_rebase_i(opts)) {
                struct todo_list done = TODO_LIST_INIT;
 -              FILE *f = fopen(rebase_path_msgtotal(), "w");
 +              FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
  
                if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
                                !parse_insn_buffer(done.buf.buf, &done))
@@@ -2130,8 -2129,8 +2130,8 @@@ cleanup_head_ref
                        if (read_oneliner(&buf, rebase_path_orig_head(), 0) &&
                            !get_sha1(buf.buf, orig.hash) &&
                            !get_sha1("HEAD", head.hash)) {
-                               diff_tree_sha1(orig.hash, head.hash,
-                                              "", &log_tree_opt.diffopt);
+                               diff_tree_oid(&orig, &head, "",
+                                             &log_tree_opt.diffopt);
                                log_tree_diff_flush(&log_tree_opt);
                        }
                }