Merge branch 'kw/patch-ids-optim'
authorJunio C Hamano <gitster@pobox.com>
Fri, 12 Aug 2016 16:47:39 +0000 (09:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 12 Aug 2016 16:47:39 +0000 (09:47 -0700)
When "git rebase" tries to compare set of changes on the updated
upstream and our own branch, it computes patch-id for all of these
changes and attempts to find matches. This has been optimized by
lazily computing the full patch-id (which is expensive) to be
compared only for changes that touch the same set of paths.

* kw/patch-ids-optim:
rebase: avoid computing unnecessary patch IDs
patch-ids: add flag to create the diff patch id using header only data
patch-ids: replace the seen indicator with a commit pointer
patch-ids: stop using a hand-rolled hashmap implementation

1  2 
builtin/log.c
diff.c
revision.c
diff --combined builtin/log.c
index 1f116bea8c8873d652f573db2047c558fa51bbd3,e36457288a71035cbcdd8b57f5ef0145204be512..92dc34dcb0cc54df27b99c91d08786b6c0d5b62a
@@@ -33,7 -33,6 +33,7 @@@ static const char *default_date_mode = 
  static int default_abbrev_commit;
  static int default_show_root = 1;
  static int default_follow;
 +static int default_show_signature;
  static int decoration_style;
  static int decoration_given;
  static int use_mailmap_config;
@@@ -120,7 -119,6 +120,7 @@@ static void cmd_log_init_defaults(struc
        rev->abbrev_commit = default_abbrev_commit;
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
 +      rev->show_signature = default_show_signature;
        DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
  
        if (default_date_mode)
@@@ -238,17 -236,16 +238,17 @@@ static void show_early_header(struct re
                if (rev->commit_format != CMIT_FMT_ONELINE)
                        putchar(rev->diffopt.line_termination);
        }
 -      printf(_("Final output: %d %s\n"), nr, stage);
 +      fprintf(rev->diffopt.file, _("Final output: %d %s\n"), nr, stage);
  }
  
  static struct itimerval early_output_timer;
  
  static void log_show_early(struct rev_info *revs, struct commit_list *list)
  {
 -      int i = revs->early_output;
 +      int i = revs->early_output, close_file = revs->diffopt.close_file;
        int show_header = 1;
  
 +      revs->diffopt.close_file = 0;
        sort_in_topological_order(&list, revs->sort_order);
        while (list && i) {
                struct commit *commit = list->item;
                case commit_ignore:
                        break;
                case commit_error:
 +                      if (close_file)
 +                              fclose(revs->diffopt.file);
                        return;
                }
                list = list->next;
        }
  
        /* Did we already get enough commits for the early output? */
 -      if (!i)
 +      if (!i) {
 +              if (close_file)
 +                      fclose(revs->diffopt.file);
                return;
 +      }
  
        /*
         * ..if no, then repeat it twice a second until we
@@@ -339,7 -331,7 +339,7 @@@ static int cmd_log_walk(struct rev_inf
  {
        struct commit *commit;
        int saved_nrl = 0;
 -      int saved_dcctc = 0;
 +      int saved_dcctc = 0, close_file = rev->diffopt.close_file;
  
        if (rev->early_output)
                setup_early_output(rev);
         * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
         * retain that state information if replacing rev->diffopt in this loop
         */
 +      rev->diffopt.close_file = 0;
        while ((commit = get_revision(rev)) != NULL) {
                if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
                        /*
        }
        rev->diffopt.degraded_cc_to_c = saved_dcctc;
        rev->diffopt.needed_rename_limit = saved_nrl;
 +      if (close_file)
 +              fclose(rev->diffopt.file);
  
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
            DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
@@@ -420,10 -409,6 +420,10 @@@ static int git_log_config(const char *v
                use_mailmap_config = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "log.showsignature")) {
 +              default_show_signature = git_config_bool(var, value);
 +              return 0;
 +      }
  
        if (grep_config(var, value, cb) < 0)
                return -1;
@@@ -460,7 -445,7 +460,7 @@@ static void show_tagger(char *buf, int 
        pp.fmt = rev->commit_format;
        pp.date_mode = rev->date_mode;
        pp_user_info(&pp, "Tagger", &out, buf, get_log_output_encoding());
 -      printf("%s", out.buf);
 +      fprintf(rev->diffopt.file, "%s", out.buf);
        strbuf_release(&out);
  }
  
@@@ -471,7 -456,7 +471,7 @@@ static int show_blob_object(const unsig
        char *buf;
        unsigned long size;
  
 -      fflush(stdout);
 +      fflush(rev->diffopt.file);
        if (!DIFF_OPT_TOUCHED(&rev->diffopt, ALLOW_TEXTCONV) ||
            !DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
                return stream_blob_to_fd(1, sha1, NULL, 0);
@@@ -511,7 -496,7 +511,7 @@@ static int show_tag_object(const unsign
        }
  
        if (offset < size)
 -              fwrite(buf + offset, size - offset, 1, stdout);
 +              fwrite(buf + offset, size - offset, 1, rev->diffopt.file);
        free(buf);
        return 0;
  }
@@@ -520,8 -505,7 +520,8 @@@ static int show_tree_object(const unsig
                struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
  {
 -      printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
 +      FILE *file = context;
 +      fprintf(file, "%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
        return 0;
  }
  
@@@ -581,7 -565,7 +581,7 @@@ int cmd_show(int argc, const char **arg
  
                        if (rev.shown_one)
                                putchar('\n');
 -                      printf("%stag %s%s\n",
 +                      fprintf(rev.diffopt.file, "%stag %s%s\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                case OBJ_TREE:
                        if (rev.shown_one)
                                putchar('\n');
 -                      printf("%stree %s%s\n\n",
 +                      fprintf(rev.diffopt.file, "%stree %s%s\n\n",
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        name,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
                        read_tree_recursive((struct tree *)o, "", 0, 0, &match_all,
 -                                      show_tree_object, NULL);
 +                                      show_tree_object, rev.diffopt.file);
                        rev.shown_one = 1;
                        break;
                case OBJ_COMMIT:
@@@ -690,9 -674,9 +690,9 @@@ static int auto_number = 1
  
  static char *default_attach = NULL;
  
 -static struct string_list extra_hdr;
 -static struct string_list extra_to;
 -static struct string_list extra_cc;
 +static struct string_list extra_hdr = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_to = STRING_LIST_INIT_NODUP;
 +static struct string_list extra_cc = STRING_LIST_INIT_NODUP;
  
  static void add_header(const char *value)
  {
  static int thread;
  static int do_signoff;
  static int base_auto;
 +static char *from;
  static const char *signature = git_version_string;
  static const char *signature_file;
  static int config_cover_letter;
@@@ -808,25 -791,15 +808,25 @@@ static int git_format_config(const cha
                base_auto = git_config_bool(var, value);
                return 0;
        }
 +      if (!strcmp(var, "format.from")) {
 +              int b = git_config_maybe_bool(var, value);
 +              free(from);
 +              if (b < 0)
 +                      from = xstrdup(value);
 +              else if (b)
 +                      from = xstrdup(git_committer_info(IDENT_NO_DATE));
 +              else
 +                      from = NULL;
 +              return 0;
 +      }
  
        return git_log_config(var, value, cb);
  }
  
 -static FILE *realstdout = NULL;
  static const char *output_directory = NULL;
  static int outdir_offset;
  
 -static int reopen_stdout(struct commit *commit, const char *subject,
 +static int open_next_file(struct commit *commit, const char *subject,
                         struct rev_info *rev, int quiet)
  {
        struct strbuf filename = STRBUF_INIT;
                fmt_output_subject(&filename, subject, rev);
  
        if (!quiet)
 -              fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
 +              printf("%s\n", filename.buf + outdir_offset);
  
 -      if (freopen(filename.buf, "w", stdout) == NULL)
 +      if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL)
                return error(_("Cannot open patch file %s"), filename.buf);
  
        strbuf_release(&filename);
@@@ -909,15 -882,15 +909,15 @@@ static void gen_message_id(struct rev_i
        info->message_id = strbuf_detach(&buf, NULL);
  }
  
 -static void print_signature(void)
 +static void print_signature(FILE *file)
  {
        if (!signature || !*signature)
                return;
  
 -      printf("-- \n%s", signature);
 +      fprintf(file, "-- \n%s", signature);
        if (signature[strlen(signature)-1] != '\n')
 -              putchar('\n');
 -      putchar('\n');
 +              putc('\n', file);
 +      putc('\n', file);
  }
  
  static void add_branch_description(struct strbuf *buf, const char *branch_name)
@@@ -980,13 -953,13 +980,13 @@@ static void make_cover_letter(struct re
        struct pretty_print_context pp = {0};
        struct commit *head = list[0];
  
 -      if (rev->commit_format != CMIT_FMT_EMAIL)
 +      if (!cmit_fmt_is_mail(rev->commit_format))
                die(_("Cover letter needs email format"));
  
        committer = git_committer_info(0);
  
        if (!use_stdout &&
 -          reopen_stdout(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
 +          open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
                return;
  
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
        pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
        pp_remainder(&pp, &msg, &sb, 0);
        add_branch_description(&sb, branch_name);
 -      printf("%s\n", sb.buf);
 +      fprintf(rev->diffopt.file, "%s\n", sb.buf);
  
        strbuf_release(&sb);
  
        log.wrap = 72;
        log.in1 = 2;
        log.in2 = 4;
 +      log.file = rev->diffopt.file;
        for (i = 0; i < nr; i++)
                shortlog_add_commit(&log, list[i]);
  
        diffcore_std(&opts);
        diff_flush(&opts);
  
 -      printf("\n");
 -      print_signature();
 +      fprintf(rev->diffopt.file, "\n");
 +      print_signature(rev->diffopt.file);
  }
  
  static const char *clean_message_id(const char *msg_id)
@@@ -1343,7 -1315,7 +1343,7 @@@ static void prepare_bases(struct base_t
                struct object_id *patch_id;
                if (commit->util)
                        continue;
-               if (commit_patch_id(commit, &diffopt, sha1))
+               if (commit_patch_id(commit, &diffopt, sha1, 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;
        }
  }
  
 -static void print_bases(struct base_tree_info *bases)
 +static void print_bases(struct base_tree_info *bases, FILE *file)
  {
        int i;
  
                return;
  
        /* Show the base commit */
 -      printf("base-commit: %s\n", oid_to_hex(&bases->base_commit));
 +      fprintf(file, "base-commit: %s\n", oid_to_hex(&bases->base_commit));
  
        /* Show the prerequisite patches */
        for (i = bases->nr_patch_id - 1; i >= 0; i--)
 -              printf("prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
 +              fprintf(file, "prerequisite-patch-id: %s\n", oid_to_hex(&bases->patch_id[i]));
  
        free(bases->patch_id);
        bases->nr_patch_id = 0;
@@@ -1396,6 -1368,7 +1396,6 @@@ int cmd_format_patch(int argc, const ch
        int quiet = 0;
        int reroll_count = -1;
        char *branch_name = NULL;
 -      char *from = NULL;
        char *base_commit = NULL;
        struct base_tree_info bases;
  
                setup_pager();
  
        if (output_directory) {
 +              if (rev.diffopt.use_color != GIT_COLOR_ALWAYS)
 +                      rev.diffopt.use_color = GIT_COLOR_NEVER;
                if (use_stdout)
                        die(_("standard output, or directory, which one?"));
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
                get_patch_ids(&rev, &ids);
        }
  
 -      if (!use_stdout)
 -              realstdout = xfdopen(xdup(1), "w");
 -
        if (prepare_revision_walk(&rev))
                die(_("revision walk setup failed"));
        rev.boundary = 1;
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout,
                                  origin, nr, list, branch_name, quiet);
 -              print_bases(&bases);
 +              print_bases(&bases, rev.diffopt.file);
                total++;
                start_number--;
        }
                }
  
                if (!use_stdout &&
 -                  reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
 +                  open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free_commit_buffer(commit);
                        rev.shown_one = 0;
                if (shown) {
                        if (rev.mime_boundary)
 -                              printf("\n--%s%s--\n\n\n",
 +                              fprintf(rev.diffopt.file, "\n--%s%s--\n\n\n",
                                       mime_boundary_leader,
                                       rev.mime_boundary);
                        else
 -                              print_signature();
 -                      print_bases(&bases);
 +                              print_signature(rev.diffopt.file);
 +                      print_bases(&bases, rev.diffopt.file);
                }
                if (!use_stdout)
 -                      fclose(stdout);
 +                      fclose(rev.diffopt.file);
        }
        free(list);
        free(branch_name);
@@@ -1820,15 -1794,15 +1820,15 @@@ static const char * const cherry_usage[
  };
  
  static void print_commit(char sign, struct commit *commit, int verbose,
 -                       int abbrev)
 +                       int abbrev, FILE *file)
  {
        if (!verbose) {
 -              printf("%c %s\n", sign,
 +              fprintf(file, "%c %s\n", sign,
                       find_unique_abbrev(commit->object.oid.hash, abbrev));
        } else {
                struct strbuf buf = STRBUF_INIT;
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
 -              printf("%c %s %s\n", sign,
 +              fprintf(file, "%c %s %s\n", sign,
                       find_unique_abbrev(commit->object.oid.hash, abbrev),
                       buf.buf);
                strbuf_release(&buf);
@@@ -1909,7 -1883,7 +1909,7 @@@ int cmd_cherry(int argc, const char **a
                commit = list->item;
                if (has_commit_patch_id(commit, &ids))
                        sign = '-';
 -              print_commit(sign, commit, verbose, abbrev);
 +              print_commit(sign, commit, verbose, abbrev, revs.diffopt.file);
                list = list->next;
        }
  
diff --combined diff.c
index b43d3dd2ecb7b2154ce6c445983e23ce89c69111,bb816d9cba6a8b46130251570fa425ff423f2ea1..534c12e28ea8b077c9fa305a634f7a73cf30589d
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -1933,8 -1933,8 +1933,8 @@@ static void show_dirstat(struct diff_op
  
                name = p->two->path ? p->two->path : p->one->path;
  
 -              if (p->one->sha1_valid && p->two->sha1_valid)
 -                      content_changed = hashcmp(p->one->sha1, p->two->sha1);
 +              if (p->one->oid_valid && p->two->oid_valid)
 +                      content_changed = oidcmp(&p->one->oid, &p->two->oid);
                else
                        content_changed = 1;
  
@@@ -2306,8 -2306,7 +2306,8 @@@ static void builtin_diff(const char *na
                const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
                show_submodule_summary(o->file, one->path ? one->path : two->path,
                                line_prefix,
 -                              one->sha1, two->sha1, two->dirty_submodule,
 +                              one->oid.hash, two->oid.hash,
 +                              two->dirty_submodule,
                                meta, del, add, reset);
                return;
        }
                if (!one->data && !two->data &&
                    S_ISREG(one->mode) && S_ISREG(two->mode) &&
                    !DIFF_OPT_TST(o, BINARY)) {
 -                      if (!hashcmp(one->sha1, two->sha1)) {
 +                      if (!oidcmp(&one->oid, &two->oid)) {
                                if (must_show_header)
                                        fprintf(o->file, "%s", header.buf);
                                goto free_ab_and_return;
@@@ -2506,7 -2505,7 +2506,7 @@@ static void builtin_diffstat(const cha
                return;
        }
  
 -      same_contents = !hashcmp(one->sha1, two->sha1);
 +      same_contents = !oidcmp(&one->oid, &two->oid);
  
        if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
                data->is_binary = 1;
@@@ -2639,8 -2638,8 +2639,8 @@@ void fill_filespec(struct diff_filespe
  {
        if (mode) {
                spec->mode = canon_mode(mode);
 -              hashcpy(spec->sha1, sha1);
 -              spec->sha1_valid = sha1_valid;
 +              hashcpy(spec->oid.hash, sha1);
 +              spec->oid_valid = sha1_valid;
        }
  }
  
@@@ -2683,13 -2682,6 +2683,13 @@@ static int reuse_worktree_file(const ch
        if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1))
                return 0;
  
 +      /*
 +       * Similarly, if we'd have to convert the file contents anyway, that
 +       * makes the optimization not worthwhile.
 +       */
 +      if (!want_file && would_convert_to_git(name))
 +              return 0;
 +
        len = strlen(name);
        pos = cache_name_pos(name, len);
        if (pos < 0)
@@@ -2729,8 -2721,7 +2729,8 @@@ static int diff_populate_gitlink(struc
        if (s->dirty_submodule)
                dirty = "-dirty";
  
 -      strbuf_addf(&buf, "Subproject commit %s%s\n", sha1_to_hex(s->sha1), dirty);
 +      strbuf_addf(&buf, "Subproject commit %s%s\n",
 +                  oid_to_hex(&s->oid), dirty);
        s->size = buf.len;
        if (size_only) {
                s->data = NULL;
@@@ -2773,8 -2764,8 +2773,8 @@@ int diff_populate_filespec(struct diff_
        if (S_ISGITLINK(s->mode))
                return diff_populate_gitlink(s, size_only);
  
 -      if (!s->sha1_valid ||
 -          reuse_worktree_file(s->path, s->sha1, 0)) {
 +      if (!s->oid_valid ||
 +          reuse_worktree_file(s->path, s->oid.hash, 0)) {
                struct strbuf buf = STRBUF_INIT;
                struct stat st;
                int fd;
        else {
                enum object_type type;
                if (size_only || (flags & CHECK_BINARY)) {
 -                      type = sha1_object_info(s->sha1, &s->size);
 +                      type = sha1_object_info(s->oid.hash, &s->size);
                        if (type < 0)
 -                              die("unable to read %s", sha1_to_hex(s->sha1));
 +                              die("unable to read %s",
 +                                  oid_to_hex(&s->oid));
                        if (size_only)
                                return 0;
                        if (s->size > big_file_threshold && s->is_binary == -1) {
                                return 0;
                        }
                }
 -              s->data = read_sha1_file(s->sha1, &type, &s->size);
 +              s->data = read_sha1_file(s->oid.hash, &type, &s->size);
                if (!s->data)
 -                      die("unable to read %s", sha1_to_hex(s->sha1));
 +                      die("unable to read %s", oid_to_hex(&s->oid));
                s->should_free = 1;
        }
        return 0;
@@@ -2873,7 -2863,7 +2873,7 @@@ void diff_free_filespec_data(struct dif
  static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
                           void *blob,
                           unsigned long size,
 -                         const unsigned char *sha1,
 +                         const struct object_id *oid,
                           int mode)
  {
        int fd;
                die_errno("unable to write temp-file");
        close_tempfile(&temp->tempfile);
        temp->name = get_tempfile_path(&temp->tempfile);
 -      sha1_to_hex_r(temp->hex, sha1);
 +      oid_to_hex_r(temp->hex, oid);
        xsnprintf(temp->mode, sizeof(temp->mode), "%06o", mode);
        strbuf_release(&buf);
        strbuf_release(&template);
@@@ -2922,8 -2912,8 +2922,8 @@@ static struct diff_tempfile *prepare_te
        }
  
        if (!S_ISGITLINK(one->mode) &&
 -          (!one->sha1_valid ||
 -           reuse_worktree_file(name, one->sha1, 1))) {
 +          (!one->oid_valid ||
 +           reuse_worktree_file(name, one->oid.hash, 1))) {
                struct stat st;
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
                        if (strbuf_readlink(&sb, name, st.st_size) < 0)
                                die_errno("readlink(%s)", name);
                        prep_temp_blob(name, temp, sb.buf, sb.len,
 -                                     (one->sha1_valid ?
 -                                      one->sha1 : null_sha1),
 -                                     (one->sha1_valid ?
 +                                     (one->oid_valid ?
 +                                      &one->oid : &null_oid),
 +                                     (one->oid_valid ?
                                        one->mode : S_IFLNK));
                        strbuf_release(&sb);
                }
                else {
                        /* we can borrow from the file in the work tree */
                        temp->name = name;
 -                      if (!one->sha1_valid)
 +                      if (!one->oid_valid)
                                sha1_to_hex_r(temp->hex, null_sha1);
                        else
 -                              sha1_to_hex_r(temp->hex, one->sha1);
 +                              sha1_to_hex_r(temp->hex, one->oid.hash);
                        /* Even though we may sometimes borrow the
                         * contents from the work tree, we always want
                         * one->mode.  mode is trustworthy even when
                if (diff_populate_filespec(one, 0))
                        die("cannot read data blob for %s", one->path);
                prep_temp_blob(name, temp, one->data, one->size,
 -                             one->sha1, one->mode);
 +                             &one->oid, one->mode);
        }
        return temp;
  }
@@@ -3075,7 -3065,7 +3075,7 @@@ static void fill_metainfo(struct strbu
        default:
                *must_show_header = 0;
        }
 -      if (one && two && hashcmp(one->sha1, two->sha1)) {
 +      if (one && two && oidcmp(&one->oid, &two->oid)) {
                int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
  
                if (DIFF_OPT_TST(o, BINARY)) {
                                abbrev = 40;
                }
                strbuf_addf(msg, "%s%sindex %s..", line_prefix, set,
 -                          find_unique_abbrev(one->sha1, abbrev));
 -              strbuf_addstr(msg, find_unique_abbrev(two->sha1, abbrev));
 +                          find_unique_abbrev(one->oid.hash, abbrev));
 +              strbuf_addstr(msg, find_unique_abbrev(two->oid.hash, abbrev));
                if (one->mode == two->mode)
                        strbuf_addf(msg, " %06o", one->mode);
                strbuf_addf(msg, "%s\n", reset);
@@@ -3141,20 -3131,20 +3141,20 @@@ static void run_diff_cmd(const char *pg
  static void diff_fill_sha1_info(struct diff_filespec *one)
  {
        if (DIFF_FILE_VALID(one)) {
 -              if (!one->sha1_valid) {
 +              if (!one->oid_valid) {
                        struct stat st;
                        if (one->is_stdin) {
 -                              hashcpy(one->sha1, null_sha1);
 +                              oidclr(&one->oid);
                                return;
                        }
                        if (lstat(one->path, &st) < 0)
                                die_errno("stat '%s'", one->path);
 -                      if (index_path(one->sha1, one->path, &st, 0))
 +                      if (index_path(one->oid.hash, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
        }
        else
 -              hashclr(one->sha1);
 +              oidclr(&one->oid);
  }
  
  static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
@@@ -3987,8 -3977,6 +3987,8 @@@ int diff_opt_parse(struct diff_options 
                if (!options->file)
                        die_errno("Could not open '%s'", path);
                options->close_file = 1;
 +              if (options->use_color != GIT_COLOR_ALWAYS)
 +                      options->use_color = GIT_COLOR_NEVER;
                return argcount;
        } else
                return 0;
@@@ -4130,9 -4118,8 +4130,9 @@@ static void diff_flush_raw(struct diff_
        fprintf(opt->file, "%s", diff_line_prefix(opt));
        if (!(opt->output_format & DIFF_FORMAT_NAME_STATUS)) {
                fprintf(opt->file, ":%06o %06o %s ", p->one->mode, p->two->mode,
 -                      diff_unique_abbrev(p->one->sha1, opt->abbrev));
 -              fprintf(opt->file, "%s ", diff_unique_abbrev(p->two->sha1, opt->abbrev));
 +                      diff_unique_abbrev(p->one->oid.hash, opt->abbrev));
 +              fprintf(opt->file, "%s ",
 +                      diff_unique_abbrev(p->two->oid.hash, opt->abbrev));
        }
        if (p->score) {
                fprintf(opt->file, "%c%03d%c", p->status, similarity_index(p),
@@@ -4181,11 -4168,11 +4181,11 @@@ int diff_unmodified_pair(struct diff_fi
        /* both are valid and point at the same path.  that is, we are
         * dealing with a change.
         */
 -      if (one->sha1_valid && two->sha1_valid &&
 -          !hashcmp(one->sha1, two->sha1) &&
 +      if (one->oid_valid && two->oid_valid &&
 +          !oidcmp(&one->oid, &two->oid) &&
            !one->dirty_submodule && !two->dirty_submodule)
                return 1; /* no change */
 -      if (!one->sha1_valid && !two->sha1_valid)
 +      if (!one->oid_valid && !two->oid_valid)
                return 1; /* both look at the same file on the filesystem. */
        return 0;
  }
@@@ -4246,7 -4233,7 +4246,7 @@@ void diff_debug_filespec(struct diff_fi
                s->path,
                DIFF_FILE_VALID(s) ? "valid" : "invalid",
                s->mode,
 -              s->sha1_valid ? sha1_to_hex(s->sha1) : "");
 +              s->oid_valid ? oid_to_hex(&s->oid) : "");
        fprintf(stderr, "queue[%d] %s size %lu\n",
                x, one ? one : "",
                s->size);
@@@ -4316,11 -4303,11 +4316,11 @@@ static void diff_resolve_rename_copy(vo
                        else
                                p->status = DIFF_STATUS_RENAMED;
                }
 -              else if (hashcmp(p->one->sha1, p->two->sha1) ||
 +              else if (oidcmp(&p->one->oid, &p->two->oid) ||
                         p->one->mode != p->two->mode ||
                         p->one->dirty_submodule ||
                         p->two->dirty_submodule ||
 -                       is_null_sha1(p->one->sha1))
 +                       is_null_oid(&p->one->oid))
                        p->status = DIFF_STATUS_MODIFIED;
                else {
                        /* This is a "no-change" entry and should not
@@@ -4462,7 -4449,7 +4462,7 @@@ static void patch_id_consume(void *priv
  }
  
  /* returns 0 upon success, and writes result into sha1 */
- static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
+ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
  {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
  
                diff_fill_sha1_info(p->one);
                diff_fill_sha1_info(p->two);
-               if (fill_mmfile(&mf1, p->one) < 0 ||
-                               fill_mmfile(&mf2, p->two) < 0)
-                       return error("unable to read files to diff");
  
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
                                        len2, p->two->path);
                git_SHA1_Update(&ctx, buffer, len1);
  
+               if (diff_header_only)
+                       continue;
+               if (fill_mmfile(&mf1, p->one) < 0 ||
+                   fill_mmfile(&mf2, p->two) < 0)
+                       return error("unable to read files to diff");
                if (diff_filespec_is_binary(p->one) ||
                    diff_filespec_is_binary(p->two)) {
 -                      git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
 -                      git_SHA1_Update(&ctx, sha1_to_hex(p->two->sha1), 40);
 +                      git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
 +                                      40);
 +                      git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
 +                                      40);
                        continue;
                }
  
        return 0;
  }
  
- int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1)
+ int diff_flush_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
  {
        struct diff_queue_struct *q = &diff_queued_diff;
        int i;
-       int result = diff_get_patch_id(options, sha1);
+       int result = diff_get_patch_id(options, sha1, diff_header_only);
  
        for (i = 0; i < q->nr; i++)
                diff_free_filepair(q->queue[i]);
@@@ -4831,7 -4820,7 +4835,7 @@@ static int diff_filespec_check_stat_unm
         */
        if (!DIFF_FILE_VALID(p->one) || /* (1) */
            !DIFF_FILE_VALID(p->two) ||
 -          (p->one->sha1_valid && p->two->sha1_valid) ||
 +          (p->one->oid_valid && p->two->oid_valid) ||
            (p->one->mode != p->two->mode) ||
            diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
            diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
@@@ -5127,9 -5116,8 +5131,9 @@@ size_t fill_textconv(struct userdiff_dr
        if (!driver->textconv)
                die("BUG: fill_textconv called with non-textconv driver");
  
 -      if (driver->textconv_cache && df->sha1_valid) {
 -              *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
 +      if (driver->textconv_cache && df->oid_valid) {
 +              *outbuf = notes_cache_get(driver->textconv_cache,
 +                                        df->oid.hash,
                                          &size);
                if (*outbuf)
                        return size;
        if (!*outbuf)
                die("unable to read files to diff");
  
 -      if (driver->textconv_cache && df->sha1_valid) {
 +      if (driver->textconv_cache && df->oid_valid) {
                /* ignore errors, as we might be in a readonly repository */
 -              notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
 +              notes_cache_put(driver->textconv_cache, df->oid.hash, *outbuf,
                                size);
                /*
                 * we could save up changes and flush them all at the end,
diff --combined revision.c
index 15873bf24d72175b026e28071e83b1db7a7ed004,233ec720735a193e1003347a3e10a56a979d6bae..8a29cb03c51be119589a763b170809b8208e1857
@@@ -846,7 -846,7 +846,7 @@@ static void cherry_pick_list(struct com
                 */
                if (left_first != !!(flags & SYMMETRIC_LEFT))
                        continue;
-               commit->util = add_commit_patch_id(commit, &ids);
+               add_commit_patch_id(commit, &ids);
        }
  
        /* either cherry_mark or cherry_pick are true */
                id = has_commit_patch_id(commit, &ids);
                if (!id)
                        continue;
-               id->seen = 1;
-               commit->object.flags |= cherry_flag;
-       }
  
-       /* Now check the original side for seen ones */
-       for (p = list; p; p = p->next) {
-               struct commit *commit = p->item;
-               struct patch_id *ent;
-               ent = commit->util;
-               if (!ent)
-                       continue;
-               if (ent->seen)
-                       commit->object.flags |= cherry_flag;
-               commit->util = NULL;
+               commit->object.flags |= cherry_flag;
+               id->commit->object.flags |= cherry_flag;
        }
  
        free_patch_ids(&ids);
@@@ -1425,7 -1413,7 +1413,7 @@@ static void prepare_show_merge(struct r
                       ce_same_name(ce, active_cache[i+1]))
                        i++;
        }
 -      free_pathspec(&revs->prune_data);
 +      clear_pathspec(&revs->prune_data);
        parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
                       PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "", prune);
        revs->limited = 1;
@@@ -1871,8 -1859,6 +1859,8 @@@ static int handle_revision_opt(struct r
                revs->notes_opt.use_default_notes = 1;
        } else if (!strcmp(arg, "--show-signature")) {
                revs->show_signature = 1;
 +      } else if (!strcmp(arg, "--no-show-signature")) {
 +              revs->show_signature = 0;
        } else if (!strcmp(arg, "--show-linear-break") ||
                   starts_with(arg, "--show-linear-break=")) {
                if (starts_with(arg, "--show-linear-break="))
        } else if (!strcmp(arg, "--grep-debug")) {
                revs->grep_filter.debug = 1;
        } else if (!strcmp(arg, "--basic-regexp")) {
 -              grep_set_pattern_type_option(GREP_PATTERN_TYPE_BRE, &revs->grep_filter);
 +              revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_BRE;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
 -              grep_set_pattern_type_option(GREP_PATTERN_TYPE_ERE, &revs->grep_filter);
 +              revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
                revs->grep_filter.regflags |= REG_ICASE;
                DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
        } else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
 -              grep_set_pattern_type_option(GREP_PATTERN_TYPE_FIXED, &revs->grep_filter);
 +              revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_FIXED;
        } else if (!strcmp(arg, "--perl-regexp")) {
 -              grep_set_pattern_type_option(GREP_PATTERN_TYPE_PCRE, &revs->grep_filter);
 +              revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_PCRE;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
        } else if (!strcmp(arg, "--invert-grep")) {