Merge branch 'rs/lose-leak-pending' into maint
authorJunio C Hamano <gitster@pobox.com>
Thu, 15 Feb 2018 23:18:11 +0000 (15:18 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 15 Feb 2018 23:18:11 +0000 (15:18 -0800)
API clean-up around revision traversal.

* rs/lose-leak-pending:
commit: remove unused function clear_commit_marks_for_object_array()
revision: remove the unused flag leak_pending
checkout: avoid using the rev_info flag leak_pending
bundle: avoid using the rev_info flag leak_pending
bisect: avoid using the rev_info flag leak_pending
object: add clear_commit_marks_all()
ref-filter: use clear_commit_marks_many() in do_merge_filter()
commit: use clear_commit_marks_many() in remove_redundant()
commit: avoid allocation in clear_commit_marks_many()

1  2 
bisect.c
builtin/checkout.c
bundle.c
commit.h
object.h
ref-filter.c
revision.c
revision.h
diff --combined bisect.c
index 2f3008b07866fb7bda6c0e0bbb089b1be1271a1a,4e78495bc2a634822330c2d1cbd2a30112c71d79..0d9e731b72692103aa17ee2e28e0126fcdf32228
+++ b/bisect.c
@@@ -229,10 -229,8 +229,10 @@@ static struct commit_list *best_bisecti
                if (i < cnt - 1)
                        p = p->next;
        }
 -      free_commit_list(p->next);
 -      p->next = NULL;
 +      if (p) {
 +              free_commit_list(p->next);
 +              p->next = NULL;
 +      }
        strbuf_release(&buf);
        free(array);
        return list;
@@@ -441,12 -439,7 +441,12 @@@ static int read_bisect_refs(void
  
  static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
  static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
 +static GIT_PATH_FUNC(git_path_bisect_ancestors_ok, "BISECT_ANCESTORS_OK")
 +static GIT_PATH_FUNC(git_path_bisect_run, "BISECT_RUN")
 +static GIT_PATH_FUNC(git_path_bisect_start, "BISECT_START")
 +static GIT_PATH_FUNC(git_path_bisect_log, "BISECT_LOG")
  static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
 +static GIT_PATH_FUNC(git_path_head_name, "head-name")
  
  static void read_bisect_paths(struct argv_array *array)
  {
@@@ -698,12 -691,11 +698,12 @@@ static int bisect_checkout(const struc
        char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
  
        memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
 -      update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev->hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 +      update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
  
        argv_checkout[2] = bisect_rev_hex;
        if (no_checkout) {
 -              update_ref(NULL, "BISECT_HEAD", bisect_rev->hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 +              update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
 +                         UPDATE_REFS_DIE_ON_ERR);
        } else {
                int res;
                res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
@@@ -792,11 -784,9 +792,9 @@@ static void handle_skipped_merge_base(c
   * - If one is "skipped", we can't know but we should warn.
   * - If we don't know, we should check it out and ask the user to test.
   */
- static void check_merge_bases(int no_checkout)
+ static void check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
  {
        struct commit_list *result;
-       int rev_nr;
-       struct commit **rev = get_bad_and_good_commits(&rev_nr);
  
        result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
  
                }
        }
  
-       free(rev);
        free_commit_list(result);
  }
  
- static int check_ancestors(const char *prefix)
+ static int check_ancestors(int rev_nr, struct commit **rev, const char *prefix)
  {
        struct rev_info revs;
-       struct object_array pending_copy;
        int res;
  
        bisect_rev_setup(&revs, prefix, "^%s", "%s", 0);
  
-       /* Save pending objects, so they can be cleaned up later. */
-       pending_copy = revs.pending;
-       revs.leak_pending = 1;
-       /*
-        * bisect_common calls prepare_revision_walk right away, which
-        * (together with .leak_pending = 1) makes us the sole owner of
-        * the list of pending objects.
-        */
        bisect_common(&revs);
        res = (revs.commits != NULL);
  
        /* Clean up objects used, as they will be reused. */
-       clear_commit_marks_for_object_array(&pending_copy, ALL_REV_FLAGS);
-       object_array_clear(&pending_copy);
+       clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
  
        return res;
  }
@@@ -858,7 -835,8 +843,8 @@@ static void check_good_are_ancestors_of
  {
        char *filename = git_pathdup("BISECT_ANCESTORS_OK");
        struct stat st;
-       int fd;
+       int fd, rev_nr;
+       struct commit **rev;
  
        if (!current_bad_oid)
                die(_("a %s revision is needed"), term_bad);
                goto done;
  
        /* Check if all good revs are ancestor of the bad rev. */
-       if (check_ancestors(prefix))
-               check_merge_bases(no_checkout);
+       rev = get_bad_and_good_commits(&rev_nr);
+       if (check_ancestors(rev_nr, rev, prefix))
+               check_merge_bases(rev_nr, rev, no_checkout);
+       free(rev);
  
        /* Create file BISECT_ANCESTORS_OK. */
        fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@@ -1057,40 -1037,3 +1045,40 @@@ int estimate_bisect_steps(int all
  
        return (e < 3 * x) ? n : n - 1;
  }
 +
 +static int mark_for_removal(const char *refname, const struct object_id *oid,
 +                          int flag, void *cb_data)
 +{
 +      struct string_list *refs = cb_data;
 +      char *ref = xstrfmt("refs/bisect%s", refname);
 +      string_list_append(refs, ref);
 +      return 0;
 +}
 +
 +int bisect_clean_state(void)
 +{
 +      int result = 0;
 +
 +      /* There may be some refs packed during bisection */
 +      struct string_list refs_for_removal = STRING_LIST_INIT_NODUP;
 +      for_each_ref_in("refs/bisect", mark_for_removal, (void *) &refs_for_removal);
 +      string_list_append(&refs_for_removal, xstrdup("BISECT_HEAD"));
 +      result = delete_refs("bisect: remove", &refs_for_removal, REF_NO_DEREF);
 +      refs_for_removal.strdup_strings = 1;
 +      string_list_clear(&refs_for_removal, 0);
 +      unlink_or_warn(git_path_bisect_expected_rev());
 +      unlink_or_warn(git_path_bisect_ancestors_ok());
 +      unlink_or_warn(git_path_bisect_log());
 +      unlink_or_warn(git_path_bisect_names());
 +      unlink_or_warn(git_path_bisect_run());
 +      unlink_or_warn(git_path_bisect_terms());
 +      /* Cleanup head-name if it got left by an old version of git-bisect */
 +      unlink_or_warn(git_path_head_name());
 +      /*
 +       * Cleanup BISECT_START last to support the --no-checkout option
 +       * introduced in the commit 4796e823a.
 +       */
 +      unlink_or_warn(git_path_bisect_start());
 +
 +      return result;
 +}
diff --combined builtin/checkout.c
index 8bdc927d3f561e62802ab53be0b3029325e6ebcd,4f61ff0aa0644660edf283e711fcdc2808c4f68c..c54c78df547c8c66377f023730958f7a95d3aea1
@@@ -1,6 -1,5 +1,6 @@@
  #include "builtin.h"
  #include "config.h"
 +#include "checkout.h"
  #include "lockfile.h"
  #include "parse-options.h"
  #include "refs.h"
@@@ -248,7 -247,7 +248,7 @@@ static int checkout_paths(const struct 
        struct object_id rev;
        struct commit *head;
        int errs = 0;
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
  
        if (opts->track != BRANCH_TRACK_UNSPECIFIED)
                die(_("'%s' cannot be used with updating paths"), "--track");
                return run_add_interactive(revision, "--patch=checkout",
                                           &opts->pathspec);
  
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(&opts->pathspec) < 0)
                return error(_("index file corrupt"));
  
        }
        errs |= finish_delayed_checkout(&state);
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
 -      read_ref_full("HEAD", 0, rev.hash, NULL);
 +      read_ref_full("HEAD", 0, &rev, NULL);
        head = lookup_commit_reference_gently(&rev, 1);
  
        errs |= post_checkout_hook(head, head, 0);
@@@ -401,16 -402,10 +401,16 @@@ static void show_local_changes(struct o
  static void describe_detached_head(const char *msg, struct commit *commit)
  {
        struct strbuf sb = STRBUF_INIT;
 +
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
 -      fprintf(stderr, "%s %s... %s\n", msg,
 -              find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
 +      if (print_sha1_ellipsis()) {
 +              fprintf(stderr, "%s %s... %s\n", msg,
 +                      find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
 +      } else {
 +              fprintf(stderr, "%s %s %s\n", msg,
 +                      find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
 +      }
        strbuf_release(&sb);
  }
  
@@@ -477,9 -472,9 +477,9 @@@ static int merge_working_tree(const str
                              int *writeout_error)
  {
        int ret;
 -      struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 +      struct lock_file lock_file = LOCK_INIT;
  
 -      hold_locked_index(lock_file, LOCK_DIE_ON_ERROR);
 +      hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
        if (read_cache_preload(NULL) < 0)
                return error(_("index file corrupt"));
  
                }
                tree = parse_tree_indirect(old->commit ?
                                           &old->commit->object.oid :
 -                                         &empty_tree_oid);
 +                                         the_hash_algo->empty_tree);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
                tree = parse_tree_indirect(&new->commit->object.oid);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
        if (!cache_tree_fully_valid(active_cache_tree))
                cache_tree_update(&the_index, WRITE_TREE_SILENT | WRITE_TREE_REPAIR);
  
 -      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        if (!opts->force && !opts->quiet)
@@@ -647,8 -642,8 +647,8 @@@ static void update_refs_for_switch(cons
                else
                        create_branch(opts->new_branch, new->name,
                                      opts->new_branch_force ? 1 : 0,
 -                                    opts->new_branch_log,
                                      opts->new_branch_force ? 1 : 0,
 +                                    opts->new_branch_log,
                                      opts->quiet,
                                      opts->track);
                new->name = opts->new_branch;
        if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
                /* Nothing to do. */
        } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
 -              update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL,
 -                         REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
 +              update_ref(msg.buf, "HEAD", &new->commit->object.oid, NULL,
 +                         REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
                        if (old->path &&
                            advice_detached_head && !opts->force_detach)
@@@ -791,7 -786,6 +791,6 @@@ static void orphaned_commit_warning(str
  {
        struct rev_info revs;
        struct object *object = &old->object;
-       struct object_array refs;
  
        init_revisions(&revs, NULL);
        setup_revisions(0, NULL, &revs, NULL);
        for_each_ref(add_pending_uninteresting_ref, &revs);
        add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
  
-       /* Save pending objects, so they can be cleaned up later. */
-       refs = revs.pending;
-       revs.leak_pending = 1;
-       /*
-        * prepare_revision_walk (together with .leak_pending = 1) makes us
-        * the sole owner of the list of pending objects.
-        */
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
        if (!(old->object.flags & UNINTERESTING))
                describe_detached_head(_("Previous HEAD position was"), old);
  
        /* Clean up objects used, as they will be reused. */
-       clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
-       object_array_clear(&refs);
+       clear_commit_marks_all(ALL_REV_FLAGS);
  }
  
  static int switch_branches(const struct checkout_opts *opts,
        struct object_id rev;
        int flag, writeout_error = 0;
        memset(&old, 0, sizeof(old));
 -      old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
 +      old.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
        if (old.path)
                old.commit = lookup_commit_reference_gently(&rev, 1);
        if (!(flag & REF_ISSYMREF))
@@@ -879,6 -863,46 +868,6 @@@ static int git_checkout_config(const ch
        return git_xmerge_config(var, value, NULL);
  }
  
 -struct tracking_name_data {
 -      /* const */ char *src_ref;
 -      char *dst_ref;
 -      struct object_id *dst_oid;
 -      int unique;
 -};
 -
 -static int check_tracking_name(struct remote *remote, void *cb_data)
 -{
 -      struct tracking_name_data *cb = cb_data;
 -      struct refspec query;
 -      memset(&query, 0, sizeof(struct refspec));
 -      query.src = cb->src_ref;
 -      if (remote_find_tracking(remote, &query) ||
 -          get_oid(query.dst, cb->dst_oid)) {
 -              free(query.dst);
 -              return 0;
 -      }
 -      if (cb->dst_ref) {
 -              free(query.dst);
 -              cb->unique = 0;
 -              return 0;
 -      }
 -      cb->dst_ref = query.dst;
 -      return 0;
 -}
 -
 -static const char *unique_tracking_name(const char *name, struct object_id *oid)
 -{
 -      struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
 -      cb_data.src_ref = xstrfmt("refs/heads/%s", name);
 -      cb_data.dst_oid = oid;
 -      for_each_remote(check_tracking_name, &cb_data);
 -      free(cb_data.src_ref);
 -      if (cb_data.unique)
 -              return cb_data.dst_ref;
 -      free(cb_data.dst_ref);
 -      return NULL;
 -}
 -
  static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
                                struct branch_info *new,
        setup_branch_path(new);
  
        if (!check_refname_format(new->path, 0) &&
 -          !read_ref(new->path, branch_rev.hash))
 +          !read_ref(new->path, &branch_rev))
                oidcpy(rev, &branch_rev);
        else
                new->path = NULL; /* not an existing branch */
@@@ -1101,7 -1125,7 +1090,7 @@@ static int checkout_branch(struct check
                struct object_id rev;
                int flag;
  
 -              if (!read_ref_full("HEAD", 0, rev.hash, &flag) &&
 +              if (!read_ref_full("HEAD", 0, &rev, &flag) &&
                    (flag & REF_ISSYMREF) && is_null_oid(&rev))
                        return switch_unborn_to_new_branch(opts);
        }
@@@ -1254,11 -1278,11 +1243,11 @@@ int cmd_checkout(int argc, const char *
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
  
 -              opts.branch_exists =
 -                      validate_new_branchname(opts.new_branch, &buf,
 -                                              !!opts.new_branch_force,
 -                                              !!opts.new_branch_force);
 -
 +              if (opts.new_branch_force)
 +                      opts.branch_exists = validate_branchname(opts.new_branch, &buf);
 +              else
 +                      opts.branch_exists =
 +                              validate_new_branchname(opts.new_branch, &buf, 0);
                strbuf_release(&buf);
        }
  
diff --combined bundle.c
index 93290962c95e6c27732f121f980d03289ac1ea6d,0d07202f8cc29ce1ec5f0045c5e7015684c7ef40..efe547e25fe2a53bd0ef7954cf3bec6d55218365
+++ b/bundle.c
@@@ -134,7 -134,6 +134,6 @@@ int verify_bundle(struct bundle_header 
        struct ref_list *p = &header->prerequisites;
        struct rev_info revs;
        const char *argv[] = {NULL, "--all", NULL};
-       struct object_array refs;
        struct commit *commit;
        int i, ret = 0, req_nr;
        const char *message = _("Repository lacks these prerequisite commits:");
        req_nr = revs.pending.nr;
        setup_revisions(2, argv, &revs, NULL);
  
-       /* Save pending objects, so they can be cleaned up later. */
-       refs = revs.pending;
-       revs.leak_pending = 1;
-       /*
-        * prepare_revision_walk (together with .leak_pending = 1) makes us
-        * the sole owner of the list of pending objects.
-        */
        if (prepare_revision_walk(&revs))
                die(_("revision walk setup failed"));
  
                if (commit->object.flags & PREREQ_MARK)
                        i--;
  
-       for (i = 0; i < req_nr; i++)
-               if (!(refs.objects[i].item->flags & SHOWN)) {
-                       if (++ret == 1)
-                               error("%s", message);
-                       error("%s %s", oid_to_hex(&refs.objects[i].item->oid),
-                               refs.objects[i].name);
-               }
+       for (i = 0; i < p->nr; i++) {
+               struct ref_list_entry *e = p->list + i;
+               struct object *o = parse_object(&e->oid);
+               assert(o); /* otherwise we'd have returned early */
+               if (o->flags & SHOWN)
+                       continue;
+               if (++ret == 1)
+                       error("%s", message);
+               error("%s %s", oid_to_hex(&e->oid), e->name);
+       }
  
        /* Clean up objects used, as they will be reused. */
-       clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
-       object_array_clear(&refs);
+       for (i = 0; i < p->nr; i++) {
+               struct ref_list_entry *e = p->list + i;
+               commit = lookup_commit_reference_gently(&e->oid, 1);
+               if (commit)
+                       clear_commit_marks(commit, ALL_REV_FLAGS);
+       }
  
        if (verbose) {
                struct ref_list *r;
@@@ -338,9 -335,9 +335,9 @@@ static int write_bundle_refs(int bundle
  
                if (e->item->flags & UNINTERESTING)
                        continue;
 -              if (dwim_ref(e->name, strlen(e->name), oid.hash, &ref) != 1)
 +              if (dwim_ref(e->name, strlen(e->name), &oid, &ref) != 1)
                        goto skip_write_ref;
 -              if (read_ref_full(e->name, RESOLVE_REF_READING, oid.hash, &flag))
 +              if (read_ref_full(e->name, RESOLVE_REF_READING, &oid, &flag))
                        flag = 0;
                display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
  
diff --combined commit.h
index 8c68ca1a5a18721538bb44fe37cdf94223f2a5b5,bdf14f0a725e38e05abf1a68470415a8655a3d73..425f4027752fb47190a53ddc72a64ebf9d928633
+++ b/commit.h
@@@ -7,7 -7,6 +7,7 @@@
  #include "decorate.h"
  #include "gpg-interface.h"
  #include "string-list.h"
 +#include "pretty.h"
  
  struct commit_list {
        struct commit *item;
@@@ -122,13 -121,93 +122,13 @@@ struct commit_list *copy_commit_list(st
  
  void free_commit_list(struct commit_list *list);
  
 -/* Commit formats */
 -enum cmit_fmt {
 -      CMIT_FMT_RAW,
 -      CMIT_FMT_MEDIUM,
 -      CMIT_FMT_DEFAULT = CMIT_FMT_MEDIUM,
 -      CMIT_FMT_SHORT,
 -      CMIT_FMT_FULL,
 -      CMIT_FMT_FULLER,
 -      CMIT_FMT_ONELINE,
 -      CMIT_FMT_EMAIL,
 -      CMIT_FMT_MBOXRD,
 -      CMIT_FMT_USERFORMAT,
 -
 -      CMIT_FMT_UNSPECIFIED
 -};
 -
 -static inline int cmit_fmt_is_mail(enum cmit_fmt fmt)
 -{
 -      return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD);
 -}
 -
  struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
  
 -struct pretty_print_context {
 -      /*
 -       * Callers should tweak these to change the behavior of pp_* functions.
 -       */
 -      enum cmit_fmt fmt;
 -      int abbrev;
 -      const char *after_subject;
 -      int preserve_subject;
 -      struct date_mode date_mode;
 -      unsigned date_mode_explicit:1;
 -      int print_email_subject;
 -      int expand_tabs_in_log;
 -      int need_8bit_cte;
 -      char *notes_message;
 -      struct reflog_walk_info *reflog_info;
 -      struct rev_info *rev;
 -      const char *output_encoding;
 -      struct string_list *mailmap;
 -      int color;
 -      struct ident_split *from_ident;
 -
 -      /*
 -       * Fields below here are manipulated internally by pp_* functions and
 -       * should not be counted on by callers.
 -       */
 -      struct string_list in_body_headers;
 -      int graph_width;
 -};
 -
 -struct userformat_want {
 -      unsigned notes:1;
 -};
 -
  extern int has_non_ascii(const char *text);
  extern const char *logmsg_reencode(const struct commit *commit,
                                   char **commit_encoding,
                                   const char *output_encoding);
 -extern void get_commit_format(const char *arg, struct rev_info *);
 -extern const char *format_subject(struct strbuf *sb, const char *msg,
 -                                const char *line_separator);
 -extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
 -extern int commit_format_is_empty(enum cmit_fmt);
  extern const char *skip_blank_lines(const char *msg);
 -extern void format_commit_message(const struct commit *commit,
 -                                const char *format, struct strbuf *sb,
 -                                const struct pretty_print_context *context);
 -extern void pretty_print_commit(struct pretty_print_context *pp,
 -                              const struct commit *commit,
 -                              struct strbuf *sb);
 -extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
 -                         struct strbuf *sb);
 -void pp_user_info(struct pretty_print_context *pp,
 -                const char *what, struct strbuf *sb,
 -                const char *line, const char *encoding);
 -void pp_title_line(struct pretty_print_context *pp,
 -                 const char **msg_p,
 -                 struct strbuf *sb,
 -                 const char *encoding,
 -                 int need_8bit_cte);
 -void pp_remainder(struct pretty_print_context *pp,
 -                const char **msg_p,
 -                struct strbuf *sb,
 -                int indent);
 -
  
  /** Removes the first commit from a list sorted by date, and adds all
   * of its parents.
@@@ -140,7 -219,6 +140,6 @@@ struct commit *pop_commit(struct commit
  
  void clear_commit_marks(struct commit *commit, unsigned int mark);
  void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark);
- void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
  
  
  enum rev_sort_order {
diff --combined object.h
index f34461d4afde1b25c30fa8ac72428955c475c5b5,1111f64dd98133e6d708c00f95454c355bebe288..87563d90562b51859da8313b105a3e7785ed9d26
+++ b/object.h
@@@ -38,7 -38,6 +38,7 @@@ struct object_array 
   * http-push.c:                            16-----19
   * commit.c:                               16-----19
   * sha1_name.c:                                     20
 + * list-objects-filter.c:                             21
   * builtin/fsck.c:  0--3
   */
  #define FLAG_BITS  27
@@@ -149,4 -148,9 +149,9 @@@ void object_array_clear(struct object_a
  
  void clear_object_flags(unsigned flags);
  
+ /*
+  * Clear the specified object flags from all in-core commit objects.
+  */
+ extern void clear_commit_marks_all(unsigned int flags);
  #endif /* OBJECT_H */
diff --combined ref-filter.c
index 3f9161707e66be86eabaf7347da23a2450ffdd9f,1d0d77c30d3df16dd32bb87bd25194683667066d..f9e25aea7a97e18b5723c8fa379da859d6dc9fcf
@@@ -76,11 -76,9 +76,11 @@@ static struct used_atom 
                char color[COLOR_MAXLEN];
                struct align align;
                struct {
 -                      enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;
 +                      enum {
 +                              RR_REF, RR_TRACK, RR_TRACKSHORT, RR_REMOTE_NAME, RR_REMOTE_REF
 +                      } option;
                        struct refname_atom refname;
 -                      unsigned int nobracket : 1;
 +                      unsigned int nobracket : 1, push : 1, push_remote : 1;
                } remote_ref;
                struct {
                        enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
@@@ -140,9 -138,6 +140,9 @@@ static void remote_ref_atom_parser(cons
        struct string_list params = STRING_LIST_INIT_DUP;
        int i;
  
 +      if (!strcmp(atom->name, "push") || starts_with(atom->name, "push:"))
 +              atom->u.remote_ref.push = 1;
 +
        if (!arg) {
                atom->u.remote_ref.option = RR_REF;
                refname_atom_parser_internal(&atom->u.remote_ref.refname,
                        atom->u.remote_ref.option = RR_TRACKSHORT;
                else if (!strcmp(s, "nobracket"))
                        atom->u.remote_ref.nobracket = 1;
 -              else {
 +              else if (!strcmp(s, "remotename")) {
 +                      atom->u.remote_ref.option = RR_REMOTE_NAME;
 +                      atom->u.remote_ref.push_remote = 1;
 +              } else if (!strcmp(s, "remoteref")) {
 +                      atom->u.remote_ref.option = RR_REMOTE_REF;
 +                      atom->u.remote_ref.push_remote = 1;
 +              } else {
                        atom->u.remote_ref.option = RR_REF;
                        refname_atom_parser_internal(&atom->u.remote_ref.refname,
                                                     arg, atom->name);
@@@ -1279,25 -1268,6 +1279,25 @@@ static void fill_remote_ref_details(str
                        *s = ">";
                else
                        *s = "<>";
 +      } else if (atom->u.remote_ref.option == RR_REMOTE_NAME) {
 +              int explicit;
 +              const char *remote = atom->u.remote_ref.push ?
 +                      pushremote_for_branch(branch, &explicit) :
 +                      remote_for_branch(branch, &explicit);
 +              if (explicit)
 +                      *s = xstrdup(remote);
 +              else
 +                      *s = "";
 +      } else if (atom->u.remote_ref.option == RR_REMOTE_REF) {
 +              int explicit;
 +              const char *merge;
 +
 +              merge = remote_ref_for_branch(branch, atom->u.remote_ref.push,
 +                                            &explicit);
 +              if (explicit)
 +                      *s = xstrdup(merge);
 +              else
 +                      *s = "";
        } else
                die("BUG: unhandled RR_* enum");
  }
@@@ -1407,20 -1377,16 +1407,20 @@@ static void populate_value(struct ref_a
                        if (refname)
                                fill_remote_ref_details(atom, refname, branch, &v->s);
                        continue;
 -              } else if (starts_with(name, "push")) {
 +              } else if (atom->u.remote_ref.push) {
                        const char *branch_name;
                        if (!skip_prefix(ref->refname, "refs/heads/",
                                         &branch_name))
                                continue;
                        branch = branch_get(branch_name);
  
 -                      refname = branch_get_push(branch, NULL);
 -                      if (!refname)
 -                              continue;
 +                      if (atom->u.remote_ref.push_remote)
 +                              refname = NULL;
 +                      else {
 +                              refname = branch_get_push(branch, NULL);
 +                              if (!refname)
 +                                      continue;
 +                      }
                        fill_remote_ref_details(atom, refname, branch, &v->s);
                        continue;
                } else if (starts_with(name, "color:")) {
@@@ -1995,8 -1961,7 +1995,7 @@@ static void do_merge_filter(struct ref_
                        free_array_item(item);
        }
  
-       for (i = 0; i < old_nr; i++)
-               clear_commit_marks(to_clear[i], ALL_REV_FLAGS);
+       clear_commit_marks_many(old_nr, to_clear, ALL_REV_FLAGS);
        clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
        free(to_clear);
  }
diff --combined revision.c
index 72f2b4572ee798f9e17d6f4fbc0d12cfcd2fe221,8b14142bb17306fae274c562fda6de624ad02267..1f7454c947a683eb2df28e51504bf67ebe8379cd
@@@ -395,16 -395,8 +395,16 @@@ static struct commit *one_relevant_pare
   * if the whole diff is removal of old data, and otherwise
   * REV_TREE_DIFFERENT (of course if the trees are the same we
   * want REV_TREE_SAME).
 - * That means that once we get to REV_TREE_DIFFERENT, we do not
 - * have to look any further.
 + *
 + * The only time we care about the distinction is when
 + * remove_empty_trees is in effect, in which case we care only about
 + * whether the whole change is REV_TREE_NEW, or if there's another type
 + * of change. Which means we can stop the diff early in either of these
 + * cases:
 + *
 + *   1. We're not using remove_empty_trees at all.
 + *
 + *   2. We saw anything except REV_TREE_NEW.
   */
  static int tree_difference = REV_TREE_SAME;
  
@@@ -415,11 -407,10 +415,11 @@@ static void file_add_remove(struct diff
                    const char *fullpath, unsigned dirty_submodule)
  {
        int diff = addremove == '+' ? REV_TREE_NEW : REV_TREE_OLD;
 +      struct rev_info *revs = options->change_fn_data;
  
        tree_difference |= diff;
 -      if (tree_difference == REV_TREE_DIFFERENT)
 -              DIFF_OPT_SET(options, HAS_CHANGES);
 +      if (!revs->remove_empty_trees || tree_difference != REV_TREE_NEW)
 +              options->flags.has_changes = 1;
  }
  
  static void file_change(struct diff_options *options,
                 unsigned old_dirty_submodule, unsigned new_dirty_submodule)
  {
        tree_difference = REV_TREE_DIFFERENT;
 -      DIFF_OPT_SET(options, HAS_CHANGES);
 +      options->flags.has_changes = 1;
  }
  
  static int rev_compare_tree(struct rev_info *revs,
        }
  
        tree_difference = REV_TREE_SAME;
 -      DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 +      revs->pruning.flags.has_changes = 0;
        if (diff_tree_oid(&t1->object.oid, &t2->object.oid, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@@ -480,7 -471,7 +480,7 @@@ static int rev_same_tree_as_empty(struc
                return 0;
  
        tree_difference = REV_TREE_SAME;
 -      DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
 +      revs->pruning.flags.has_changes = 0;
        retval = diff_tree_oid(NULL, &t1->object.oid, "", &revs->pruning);
  
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
@@@ -1412,11 -1403,10 +1412,11 @@@ void init_revisions(struct rev_info *re
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
 -      DIFF_OPT_SET(&revs->pruning, RECURSIVE);
 -      DIFF_OPT_SET(&revs->pruning, QUICK);
 +      revs->pruning.flags.recursive = 1;
 +      revs->pruning.flags.quick = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
 +      revs->pruning.change_fn_data = revs;
        revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
        revs->dense = 1;
        revs->prefix = prefix;
@@@ -1832,7 -1822,7 +1832,7 @@@ static int handle_revision_opt(struct r
                revs->simplify_by_decoration = 1;
                revs->limited = 1;
                revs->prune = 1;
 -              load_ref_decorations(DECORATE_SHORT_REFS);
 +              load_ref_decorations(NULL, DECORATE_SHORT_REFS);
        } else if (!strcmp(arg, "--date-order")) {
                revs->sort_order = REV_SORT_BY_COMMIT_DATE;
                revs->topo_order = 1;
                revs->dense = 0;
        } else if (!strcmp(arg, "--show-all")) {
                revs->show_all = 1;
 +      } else if (!strcmp(arg, "--in-commit-order")) {
 +              revs->tree_blobs_in_commit_order = 1;
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
        } else if (!strcmp(arg, "--merges")) {
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
 -              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 +              revs->diffopt.flags.recursive = 1;
        } else if (!strcmp(arg, "-t")) {
                revs->diff = 1;
 -              DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
 -              DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
 +              revs->diffopt.flags.recursive = 1;
 +              revs->diffopt.flags.tree_in_recursive = 1;
        } else if (!strcmp(arg, "-m")) {
                revs->ignore_merges = 0;
        } else if (!strcmp(arg, "-c")) {
                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;
 -              DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
 +              revs->diffopt.flags.pickaxe_ignore_case = 1;
        } 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") || !strcmp(arg, "-P")) {
@@@ -2411,7 -2399,7 +2411,7 @@@ int setup_revisions(int argc, const cha
        /* Pickaxe, diff-filter and rename following need diffs */
        if (revs->diffopt.pickaxe ||
            revs->diffopt.filter ||
 -          DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 +          revs->diffopt.flags.follow_renames)
                revs->diff = 1;
  
        if (revs->topo_order)
        if (revs->prune_data.nr) {
                copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
 -              if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 +              if (!revs->diffopt.flags.follow_renames)
                        revs->prune = 1;
                if (!revs->full_diff)
                        copy_pathspec(&revs->diffopt.pathspec,
@@@ -2862,8 -2850,7 +2862,7 @@@ int prepare_revision_walk(struct rev_in
                        }
                }
        }
-       if (!revs->leak_pending)
-               object_array_clear(&old_pending);
+       object_array_clear(&old_pending);
  
        /* Signal whether we need per-parent treesame decoration */
        if (revs->simplify_merges ||
diff --combined revision.h
index 19dc9bdddba20dfa6ef1bdaaee0e82db3bdf8bd8,27be70e75cdc1afd3ad45cddeb6e5e8525ce4ac2..d7a35c8c9e752cea0ea05ee7e13d696814470a83
@@@ -4,7 -4,7 +4,7 @@@
  #include "parse-options.h"
  #include "grep.h"
  #include "notes.h"
 -#include "commit.h"
 +#include "pretty.h"
  #include "diff.h"
  
  /* Remember to update object flag allocation in object.h */
@@@ -121,8 -121,7 +121,8 @@@ struct rev_info 
                        bisect:1,
                        ancestry_path:1,
                        first_parent_only:1,
 -                      line_level_traverse:1;
 +                      line_level_traverse:1,
 +                      tree_blobs_in_commit_order:1;
  
        /* Diff flags */
        unsigned int    diff:1,
                        date_mode_explicit:1,
                        preserve_subject:1;
        unsigned int    disable_stdin:1;
-       /*
-        * Set `leak_pending` to prevent `prepare_revision_walk()` from clearing
-        * the array of pending objects (`pending`). It will still forget about
-        * the array and its entries, so they really are leaked. This can be
-        * useful if the `struct object_array` `pending` is copied before
-        * calling `prepare_revision_walk()`. By setting `leak_pending`, you
-        * effectively claim ownership of the old array, so you should most
-        * likely call `object_array_clear(&pending_copy)` once you are done.
-        * Observe that this is about ownership of the array and its entries,
-        * not the commits referenced by those entries.
-        */
-       unsigned int    leak_pending:1;
        /* --show-linear-break */
        unsigned int    track_linear:1,
                        track_first_time:1,