Merge branch 'rs/pending'
authorJunio C Hamano <gitster@pobox.com>
Fri, 14 Oct 2011 02:03:22 +0000 (19:03 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 14 Oct 2011 02:03:22 +0000 (19:03 -0700)
* rs/pending:
commit: factor out clear_commit_marks_for_object_array
checkout: use leak_pending flag
bundle: use leak_pending flag
bisect: use leak_pending flag
revision: add leak_pending flag
checkout: use add_pending_{object,sha1} in orphan check
revision: factor out add_pending_sha1
checkout: check for "Previous HEAD" notice in t2020

Conflicts:
builtin/checkout.c
revision.c

1  2 
bisect.c
builtin/checkout.c
bundle.c
commit.c
commit.h
revision.c
revision.h
diff --combined bisect.c
index de05bf824620324e42b7a27571ecd3e763cf7e8f,54674fc2487093845486e8ce60ec8eda804bbb90..6e186e29cc4a6a74944798e8c4248219e9c5997f
+++ b/bisect.c
  #include "log-tree.h"
  #include "bisect.h"
  #include "sha1-array.h"
 +#include "argv-array.h"
  
  static struct sha1_array good_revs;
  static struct sha1_array skipped_revs;
  
  static const unsigned char *current_bad_sha1;
  
 -struct argv_array {
 -      const char **argv;
 -      int argv_nr;
 -      int argv_alloc;
 -};
 -
  static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
  static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
 +static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
  
  /* bits #0-15 in revision.h */
  
@@@ -400,6 -404,21 +400,6 @@@ struct commit_list *find_bisection(stru
        return best;
  }
  
 -static void argv_array_push(struct argv_array *array, const char *string)
 -{
 -      ALLOC_GROW(array->argv, array->argv_nr + 1, array->argv_alloc);
 -      array->argv[array->argv_nr++] = string;
 -}
 -
 -static void argv_array_push_sha1(struct argv_array *array,
 -                               const unsigned char *sha1,
 -                               const char *format)
 -{
 -      struct strbuf buf = STRBUF_INIT;
 -      strbuf_addf(&buf, format, sha1_to_hex(sha1));
 -      argv_array_push(array, strbuf_detach(&buf, NULL));
 -}
 -
  static int register_ref(const char *refname, const unsigned char *sha1,
                        int flags, void *cb_data)
  {
@@@ -429,10 -448,16 +429,10 @@@ static void read_bisect_paths(struct ar
                die_errno("Could not open file '%s'", filename);
  
        while (strbuf_getline(&str, fp, '\n') != EOF) {
 -              char *quoted;
 -              int res;
 -
                strbuf_trim(&str);
 -              quoted = strbuf_detach(&str, NULL);
 -              res = sq_dequote_to_argv(quoted, &array->argv,
 -                                       &array->argv_nr, &array->argv_alloc);
 -              if (res)
 +              if (sq_dequote_to_argv_array(str.buf, array))
                        die("Badly quoted content in file '%s': %s",
 -                          filename, quoted);
 +                          filename, str.buf);
        }
  
        strbuf_release(&str);
@@@ -597,7 -622,7 +597,7 @@@ static void bisect_rev_setup(struct rev
                             const char *bad_format, const char *good_format,
                             int read_paths)
  {
 -      struct argv_array rev_argv = { NULL, 0, 0 };
 +      struct argv_array rev_argv = ARGV_ARRAY_INIT;
        int i;
  
        init_revisions(revs, prefix);
        revs->commit_format = CMIT_FMT_UNSPECIFIED;
  
        /* rev_argv.argv[0] will be ignored by setup_revisions */
 -      argv_array_push(&rev_argv, xstrdup("bisect_rev_setup"));
 -      argv_array_push_sha1(&rev_argv, current_bad_sha1, bad_format);
 +      argv_array_push(&rev_argv, "bisect_rev_setup");
 +      argv_array_pushf(&rev_argv, bad_format, sha1_to_hex(current_bad_sha1));
        for (i = 0; i < good_revs.nr; i++)
 -              argv_array_push_sha1(&rev_argv, good_revs.sha1[i],
 -                                   good_format);
 -      argv_array_push(&rev_argv, xstrdup("--"));
 +              argv_array_pushf(&rev_argv, good_format,
 +                               sha1_to_hex(good_revs.sha1[i]));
 +      argv_array_push(&rev_argv, "--");
        if (read_paths)
                read_bisect_paths(&rev_argv);
 -      argv_array_push(&rev_argv, NULL);
  
 -      setup_revisions(rev_argv.argv_nr, rev_argv.argv, revs, NULL);
 +      setup_revisions(rev_argv.argc, rev_argv.argv, revs, NULL);
 +      /* XXX leak rev_argv, as "revs" may still be pointing to it */
  }
  
  static void bisect_common(struct rev_info *revs)
@@@ -682,23 -707,16 +682,23 @@@ static void mark_expected_rev(char *bis
                die("closing file %s: %s", filename, strerror(errno));
  }
  
 -static int bisect_checkout(char *bisect_rev_hex)
 +static int bisect_checkout(char *bisect_rev_hex, int no_checkout)
  {
        int res;
  
        mark_expected_rev(bisect_rev_hex);
  
        argv_checkout[2] = bisect_rev_hex;
 -      res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
 -      if (res)
 -              exit(res);
 +      if (no_checkout) {
 +              argv_update_ref[3] = bisect_rev_hex;
 +              if (run_command_v_opt(argv_update_ref, RUN_GIT_CMD))
 +                      die("update-ref --no-deref HEAD failed on %s",
 +                          bisect_rev_hex);
 +      } else {
 +              res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
 +              if (res)
 +                      exit(res);
 +      }
  
        argv_show_branch[1] = bisect_rev_hex;
        return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
@@@ -770,7 -788,7 +770,7 @@@ 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(void)
 +static void check_merge_bases(int no_checkout)
  {
        struct commit_list *result;
        int rev_nr;
                        handle_skipped_merge_base(mb);
                } else {
                        printf("Bisecting: a merge base must be tested\n");
 -                      exit(bisect_checkout(sha1_to_hex(mb)));
 +                      exit(bisect_checkout(sha1_to_hex(mb), no_checkout));
                }
        }
  
@@@ -800,25 -818,25 +800,25 @@@ static int check_ancestors(const char *
  {
        struct rev_info revs;
        struct object_array pending_copy;
-       int i, res;
+       int res;
  
        bisect_rev_setup(&revs, prefix, "^%s", "%s", 0);
  
        /* Save pending objects, so they can be cleaned up later. */
-       memset(&pending_copy, 0, sizeof(pending_copy));
-       for (i = 0; i < revs.pending.nr; i++)
-               add_object_array(revs.pending.objects[i].item,
-                                revs.pending.objects[i].name,
-                                &pending_copy);
+       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. */
-       for (i = 0; i < pending_copy.nr; i++) {
-               struct object *o = pending_copy.objects[i].item;
-               clear_commit_marks((struct commit *)o, ALL_REV_FLAGS);
-       }
+       clear_commit_marks_for_object_array(&pending_copy, ALL_REV_FLAGS);
+       free(pending_copy.objects);
  
        return res;
  }
   * If a merge base must be tested by the user, its source code will be
   * checked out to be tested by the user and we will exit.
   */
 -static void check_good_are_ancestors_of_bad(const char *prefix)
 +static void check_good_are_ancestors_of_bad(const char *prefix, int no_checkout)
  {
        const char *filename = git_path("BISECT_ANCESTORS_OK");
        struct stat st;
  
        /* Check if all good revs are ancestor of the bad rev. */
        if (check_ancestors(prefix))
 -              check_merge_bases();
 +              check_merge_bases(no_checkout);
  
        /* Create file BISECT_ANCESTORS_OK. */
        fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
@@@ -890,11 -908,8 +890,11 @@@ static void show_diff_tree(const char *
   * We use the convention that exiting with an exit code 10 means that
   * the bisection process finished successfully.
   * In this case the calling shell script should exit 0.
 + *
 + * If no_checkout is non-zero, the bisection process does not
 + * checkout the trial commit but instead simply updates BISECT_HEAD.
   */
 -int bisect_next_all(const char *prefix)
 +int bisect_next_all(const char *prefix, int no_checkout)
  {
        struct rev_info revs;
        struct commit_list *tried;
        if (read_bisect_refs())
                die("reading bisect refs failed");
  
 -      check_good_are_ancestors_of_bad(prefix);
 +      check_good_are_ancestors_of_bad(prefix, no_checkout);
  
        bisect_rev_setup(&revs, prefix, "%s", "^%s", 1);
        revs.limited = 1;
               "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
               steps, (steps == 1 ? "" : "s"));
  
 -      return bisect_checkout(bisect_rev_hex);
 +      return bisect_checkout(bisect_rev_hex, no_checkout);
  }
  
diff --combined builtin/checkout.c
index 04df4d786ecf9fe541b62e89d85f6b0ae684b439,cefa51d515c0b472ed94328f587fcec1aaa5f3ad..49a547a0d5b787d4e9bdc3a0c772736865f2ab62
@@@ -19,7 -19,6 +19,7 @@@
  #include "ll-merge.h"
  #include "resolve-undo.h"
  #include "submodule.h"
 +#include "argv-array.h"
  
  static const char * const checkout_usage[] = {
        "git checkout [options] <branch>",
@@@ -202,7 -201,7 +202,7 @@@ static int checkout_merged(int pos, str
  }
  
  static int checkout_paths(struct tree *source_tree, const char **pathspec,
 -                        struct checkout_opts *opts)
 +                        const char *prefix, struct checkout_opts *opts)
  {
        int pos;
        struct checkout state;
                match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
        }
  
 -      if (report_path_error(ps_matched, pathspec, 0))
 +      if (report_path_error(ps_matched, pathspec, prefix))
                return 1;
  
        /* "checkout -m path" to recreate conflicted state */
@@@ -589,23 -588,11 +589,11 @@@ static void update_refs_for_switch(stru
                report_tracking(new);
  }
  
- static int add_one_ref_to_rev_list_arg(const char *refname,
-                                      const unsigned char *sha1,
-                                      int flags,
-                                      void *cb_data)
+ static int add_pending_uninteresting_ref(const char *refname,
+                                        const unsigned char *sha1,
+                                        int flags, void *cb_data)
  {
-       argv_array_push(cb_data, refname);
-       return 0;
- }
- static int clear_commit_marks_from_one_ref(const char *refname,
-                                     const unsigned char *sha1,
-                                     int flags,
-                                     void *cb_data)
- {
-       struct commit *commit = lookup_commit_reference_gently(sha1, 1);
-       if (commit)
-               clear_commit_marks(commit, -1);
+       add_pending_sha1(cb_data, refname, sha1, flags | UNINTERESTING);
        return 0;
  }
  
@@@ -646,25 -633,24 +634,25 @@@ static void suggest_reattach(struct com
                "Warning: you are leaving %d commit behind, "
                "not connected to\n"
                "any of your branches:\n\n"
 -              "%s\n"
 -              "If you want to keep it by creating a new branch, "
 -              "this may be a good time\nto do so with:\n\n"
 -              " git branch new_branch_name %s\n\n",
 +              "%s\n",
                /* The plural version */
                "Warning: you are leaving %d commits behind, "
                "not connected to\n"
                "any of your branches:\n\n"
 -              "%s\n"
 -              "If you want to keep them by creating a new branch, "
 -              "this may be a good time\nto do so with:\n\n"
 -              " git branch new_branch_name %s\n\n",
 +              "%s\n",
                /* Give ngettext() the count */
                lost),
                lost,
 -              sb.buf,
 -              sha1_to_hex(commit->object.sha1));
 +              sb.buf);
        strbuf_release(&sb);
 +
 +      if (advice_detached_head)
 +              fprintf(stderr,
 +                      _(
 +                      "If you want to keep them by creating a new branch, "
 +                      "this may be a good time\nto do so with:\n\n"
 +                      " git branch new_branch_name %s\n\n"),
 +                      sha1_to_hex(commit->object.sha1));
  }
  
  /*
   */
  static void orphaned_commit_warning(struct commit *commit)
  {
-       struct argv_array args = ARGV_ARRAY_INIT;
        struct rev_info revs;
-       argv_array_push(&args, "(internal)");
-       argv_array_push(&args, sha1_to_hex(commit->object.sha1));
-       argv_array_push(&args, "--not");
-       for_each_ref(add_one_ref_to_rev_list_arg, &args);
-       argv_array_push(&args, "--");
+       struct object *object = &commit->object;
+       struct object_array refs;
  
        init_revisions(&revs, NULL);
-       if (setup_revisions(args.argc - 1, args.argv, &revs, NULL) != 1)
-               die(_("internal error: only -- alone should have been left"));
+       setup_revisions(0, NULL, &revs, NULL);
+       object->flags &= ~UNINTERESTING;
+       add_pending_object(&revs, object, sha1_to_hex(object->sha1));
+       for_each_ref(add_pending_uninteresting_ref, &revs);
+       refs = revs.pending;
+       revs.leak_pending = 1;
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
        if (!(commit->object.flags & UNINTERESTING))
        else
                describe_detached_head(_("Previous HEAD position was"), commit);
  
-       argv_array_clear(&args);
-       clear_commit_marks(commit, -1);
-       for_each_ref(clear_commit_marks_from_one_ref, NULL);
+       clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
+       free(refs.objects);
  }
  
  static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
        unsigned char rev[20];
        int flag;
        memset(&old, 0, sizeof(old));
 -      old.path = resolve_ref("HEAD", rev, 0, &flag);
 +      old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
        old.commit = lookup_commit_reference_gently(rev, 1);
 -      if (!(flag & REF_ISSYMREF))
 +      if (!(flag & REF_ISSYMREF)) {
 +              free((char *)old.path);
                old.path = NULL;
 +      }
  
        if (old.path && !prefixcmp(old.path, "refs/heads/"))
                old.name = old.path + strlen("refs/heads/");
        update_refs_for_switch(opts, &old, new);
  
        ret = post_checkout_hook(old.commit, new->commit, 1);
 +      free((char *)old.path);
        return ret || opts->writeout_error;
  }
  
@@@ -871,7 -856,7 +861,7 @@@ static int parse_branchname_arg(int arg
        new->name = arg;
        setup_branch_path(new);
  
 -      if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK &&
 +      if (!check_refname_format(new->path, 0) &&
            resolve_ref(new->path, branch_rev, 1, NULL))
                hashcpy(rev, branch_rev);
        else
@@@ -1053,7 -1038,7 +1043,7 @@@ int cmd_checkout(int argc, const char *
                if (1 < !!opts.writeout_stage + !!opts.force + !!opts.merge)
                        die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\nchecking out of the index."));
  
 -              return checkout_paths(source_tree, pathspec, &opts);
 +              return checkout_paths(source_tree, pathspec, prefix, &opts);
        }
  
        if (patch_mode)
  
        if (opts.new_branch) {
                struct strbuf buf = STRBUF_INIT;
 -              if (strbuf_check_branch_ref(&buf, opts.new_branch))
 -                      die(_("git checkout: we do not like '%s' as a branch name."),
 -                          opts.new_branch);
 -              if (!get_sha1(buf.buf, rev)) {
 -                      opts.branch_exists = 1;
 -                      if (!opts.new_branch_force)
 -                              die(_("git checkout: branch %s already exists"),
 -                                  opts.new_branch);
 -              }
 +
 +              opts.branch_exists = validate_new_branchname(opts.new_branch, &buf,
 +                                                           !!opts.new_branch_force, 0);
 +
                strbuf_release(&buf);
        }
  
diff --combined bundle.c
index 6bf849740c6de45fe8ca11eca2a06a52c7ebc0bb,a8ea918c08c92077a532185480d8693fa21bc4dc..f82baae3bd2736cd0abca6b2412e3c4fd363b1e6
+++ b/bundle.c
@@@ -122,11 -122,8 +122,8 @@@ int verify_bundle(struct bundle_header 
        req_nr = revs.pending.nr;
        setup_revisions(2, argv, &revs, NULL);
  
-       memset(&refs, 0, sizeof(struct object_array));
-       for (i = 0; i < revs.pending.nr; i++) {
-               struct object_array_entry *e = revs.pending.objects + i;
-               add_object_array(e->item, e->name, &refs);
-       }
+       refs = revs.pending;
+       revs.leak_pending = 1;
  
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
                                refs.objects[i].name);
                }
  
-       for (i = 0; i < refs.nr; i++)
-               clear_commit_marks((struct commit *)refs.objects[i].item, -1);
+       clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
+       free(refs.objects);
  
        if (verbose) {
                struct ref_list *r;
@@@ -380,15 -377,12 +377,15 @@@ int create_bundle(struct bundle_header 
        return 0;
  }
  
 -int unbundle(struct bundle_header *header, int bundle_fd)
 +int unbundle(struct bundle_header *header, int bundle_fd, int flags)
  {
        const char *argv_index_pack[] = {"index-pack",
 -              "--fix-thin", "--stdin", NULL};
 +                                       "--fix-thin", "--stdin", NULL, NULL};
        struct child_process ip;
  
 +      if (flags & BUNDLE_VERBOSE)
 +              argv_index_pack[3] = "-v";
 +
        if (verify_bundle(header, 0))
                return -1;
        memset(&ip, 0, sizeof(ip));
diff --combined commit.c
index 9f4cc636dd74e99a1311500e15501bdb4875cb9d,4d80f2522c084c80ee7e75831cdd3d52e111f1ae..73b7e00292ba2de33fa43b5f028fd807a460af34
+++ b/commit.c
@@@ -39,18 -39,6 +39,18 @@@ struct commit *lookup_commit_reference(
        return lookup_commit_reference_gently(sha1, 0);
  }
  
 +struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name)
 +{
 +      struct commit *c = lookup_commit_reference(sha1);
 +      if (!c)
 +              die(_("could not parse %s"), ref_name);
 +      if (hashcmp(sha1, c->object.sha1)) {
 +              warning(_("%s %s is not a commit!"),
 +                      ref_name, sha1_to_hex(sha1));
 +      }
 +      return c;
 +}
 +
  struct commit *lookup_commit(const unsigned char *sha1)
  {
        struct object *obj = lookup_object(sha1);
@@@ -226,12 -214,22 +226,12 @@@ struct commit_graft *lookup_commit_graf
        return commit_graft[pos];
  }
  
 -int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
 +int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
  {
 -      int i, count = 0;
 -      for (i = 0; i < commit_graft_nr; i++)
 -              if (commit_graft[i]->nr_parent < 0) {
 -                      const char *hex =
 -                              sha1_to_hex(commit_graft[i]->sha1);
 -                      count++;
 -                      if (use_pack_protocol)
 -                              packet_buf_write(out, "shallow %s", hex);
 -                      else {
 -                              strbuf_addstr(out, hex);
 -                              strbuf_addch(out, '\n');
 -                      }
 -              }
 -      return count;
 +      int i, ret;
 +      for (i = ret = 0; i < commit_graft_nr && !ret; i++)
 +              ret = fn(commit_graft[i], cb_data);
 +      return ret;
  }
  
  int unregister_shallow(const unsigned char *sha1)
@@@ -442,6 -440,20 +442,20 @@@ void clear_commit_marks(struct commit *
        }
  }
  
+ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
+ {
+       struct object *object;
+       struct commit *commit;
+       unsigned int i;
+       for (i = 0; i < a->nr; i++) {
+               object = a->objects[i].item;
+               commit = lookup_commit_reference_gently(object->sha1, 1);
+               if (commit)
+                       clear_commit_marks(commit, mark);
+       }
+ }
  struct commit *pop_commit(struct commit_list **stack)
  {
        struct commit_list *top = *stack;
@@@ -517,7 -529,7 +531,7 @@@ void sort_in_topological_order(struct c
  
                commit = work_item->item;
                for (parents = commit->parents; parents ; parents = parents->next) {
 -                      struct commit *parent=parents->item;
 +                      struct commit *parent = parents->item;
  
                        if (!parent->indegree)
                                continue;
diff --combined commit.h
index 14f6a5a2ed91e2176cce4543798774f88a8628b1,641f70fb1ee271258e992c04eb33501fa91bcf3f..009b113e5bb5d04bdfb116897cc17dc5f5a2fa9c
+++ b/commit.h
@@@ -38,13 -38,6 +38,13 @@@ struct commit *lookup_commit_reference_
                                              int quiet);
  struct commit *lookup_commit_reference_by_name(const char *name);
  
 +/*
 + * Look up object named by "sha1", dereference tag as necessary,
 + * get a commit and return it. If "sha1" does not dereference to
 + * a commit, use ref_name to report an error and die.
 + */
 +struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name);
 +
  int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
  int parse_commit(struct commit *item);
  
@@@ -133,6 -126,7 +133,7 @@@ struct commit *pop_most_recent_commit(s
  struct commit *pop_commit(struct commit_list **stack);
  
  void clear_commit_marks(struct commit *commit, unsigned int mark);
+ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
  
  /*
   * Performs an in-place topological sort of list supplied.
@@@ -149,7 -143,6 +150,7 @@@ struct commit_graft 
        int nr_parent; /* < 0 if shallow commit */
        unsigned char parent[FLEX_ARRAY][20]; /* more */
  };
 +typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
  
  struct commit_graft *read_graft_line(char *buf, int len);
  int register_commit_graft(struct commit_graft *, int);
@@@ -161,7 -154,7 +162,7 @@@ extern struct commit_list *get_octopus_
  
  extern int register_shallow(const unsigned char *sha1);
  extern int unregister_shallow(const unsigned char *sha1);
 -extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol);
 +extern int for_each_commit_graft(each_commit_graft_fn, void *);
  extern int is_repository_shallow(void);
  extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
diff --combined revision.c
index 66a882029f194cf8c99704df2ca61597a2817eee,6d329b46dc16828b78c975bc1619d8aa0fcc5b48..8764dde381111cfc9c8ea7eb3856223de9786ec9
@@@ -40,47 -40,6 +40,47 @@@ char *path_name(const struct name_path 
        return n;
  }
  
 +static int show_path_component_truncated(FILE *out, const char *name, int len)
 +{
 +      int cnt;
 +      for (cnt = 0; cnt < len; cnt++) {
 +              int ch = name[cnt];
 +              if (!ch || ch == '\n')
 +                      return -1;
 +              fputc(ch, out);
 +      }
 +      return len;
 +}
 +
 +static int show_path_truncated(FILE *out, const struct name_path *path)
 +{
 +      int emitted, ours;
 +
 +      if (!path)
 +              return 0;
 +      emitted = show_path_truncated(out, path->up);
 +      if (emitted < 0)
 +              return emitted;
 +      if (emitted)
 +              fputc('/', out);
 +      ours = show_path_component_truncated(out, path->elem, path->elem_len);
 +      if (ours < 0)
 +              return ours;
 +      return ours || emitted;
 +}
 +
 +void show_object_with_name(FILE *out, struct object *obj, const struct name_path *path, const char *component)
 +{
 +      struct name_path leaf;
 +      leaf.up = (struct name_path *)path;
 +      leaf.elem = component;
 +      leaf.elem_len = strlen(component);
 +
 +      fprintf(out, "%s ", sha1_to_hex(obj->sha1));
 +      show_path_truncated(out, &leaf);
 +      fputc('\n', out);
 +}
 +
  void add_object(struct object *obj,
                struct object_array *p,
                struct name_path *path,
@@@ -226,6 -185,13 +226,13 @@@ static struct object *get_reference(str
        return object;
  }
  
+ void add_pending_sha1(struct rev_info *revs, const char *name,
+                     const unsigned char *sha1, unsigned int flags)
+ {
+       struct object *object = get_reference(revs, name, sha1, flags);
+       add_pending_object(revs, object, name);
+ }
  static struct commit *handle_commit(struct rev_info *revs, struct object *object, const char *name)
  {
        unsigned long flags = object->flags;
@@@ -770,16 -736,12 +777,16 @@@ static void limit_to_ancestry(struct co
   * to filter the result of "A..B" further to the ones that can actually
   * reach A.
   */
 -static struct commit_list *collect_bottom_commits(struct commit_list *list)
 +static struct commit_list *collect_bottom_commits(struct rev_info *revs)
  {
 -      struct commit_list *elem, *bottom = NULL;
 -      for (elem = list; elem; elem = elem->next)
 -              if (elem->item->object.flags & UNINTERESTING)
 -                      commit_list_insert(elem->item, &bottom);
 +      struct commit_list *bottom = NULL;
 +      int i;
 +      for (i = 0; i < revs->cmdline.nr; i++) {
 +              struct rev_cmdline_entry *elem = &revs->cmdline.rev[i];
 +              if ((elem->flags & UNINTERESTING) &&
 +                  elem->item->type == OBJ_COMMIT)
 +                      commit_list_insert((struct commit *)elem->item, &bottom);
 +      }
        return bottom;
  }
  
@@@ -810,7 -772,7 +817,7 @@@ static int limit_list(struct rev_info *
        struct commit_list *bottom = NULL;
  
        if (revs->ancestry_path) {
 -              bottom = collect_bottom_commits(list);
 +              bottom = collect_bottom_commits(revs);
                if (!bottom)
                        die("--ancestry-path given but there are no bottom commits");
        }
        return 0;
  }
  
 +static void add_rev_cmdline(struct rev_info *revs,
 +                          struct object *item,
 +                          const char *name,
 +                          int whence,
 +                          unsigned flags)
 +{
 +      struct rev_cmdline_info *info = &revs->cmdline;
 +      int nr = info->nr;
 +
 +      ALLOC_GROW(info->rev, nr + 1, info->alloc);
 +      info->rev[nr].item = item;
 +      info->rev[nr].name = name;
 +      info->rev[nr].whence = whence;
 +      info->rev[nr].flags = flags;
 +      info->nr++;
 +}
 +
  struct all_refs_cb {
        int all_flags;
        int warned_bad_reflog;
  static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
  {
        struct all_refs_cb *cb = cb_data;
-       add_pending_object(cb->all_revs, object, path);
 +      struct object *object = get_reference(cb->all_revs, path, sha1,
 +                                            cb->all_flags);
 +      add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
+       add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
        return 0;
  }
  
@@@ -923,7 -865,6 +930,7 @@@ static void handle_one_reflog_commit(un
                struct object *o = parse_object(sha1);
                if (o) {
                        o->flags |= cb->all_flags;
 +                      /* ??? CMDLINEFLAGS ??? */
                        add_pending_object(cb->all_revs, o, "");
                }
                else if (!cb->warned_bad_reflog) {
@@@ -960,13 -901,12 +967,13 @@@ static void handle_reflog(struct rev_in
        for_each_reflog(handle_one_reflog, &cb);
  }
  
 -static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
 +static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
  {
        unsigned char sha1[20];
        struct object *it;
        struct commit *commit;
        struct commit_list *parents;
 +      const char *arg = arg_;
  
        if (*arg == '^') {
                flags ^= UNINTERESTING;
        for (parents = commit->parents; parents; parents = parents->next) {
                it = &parents->item->object;
                it->flags |= flags;
 +              add_rev_cmdline(revs, it, arg_, REV_CMD_PARENTS_ONLY, flags);
                add_pending_object(revs, it, arg);
        }
        return 1;
@@@ -1052,12 -991,10 +1059,12 @@@ static void prepare_show_merge(struct r
        const char **prune = NULL;
        int i, prune_num = 1; /* counting terminating NULL */
  
 -      if (get_sha1("HEAD", sha1) || !(head = lookup_commit(sha1)))
 +      if (get_sha1("HEAD", sha1))
                die("--merge without HEAD?");
 -      if (get_sha1("MERGE_HEAD", sha1) || !(other = lookup_commit(sha1)))
 +      head = lookup_commit_or_die(sha1, "HEAD");
 +      if (get_sha1("MERGE_HEAD", sha1))
                die("--merge without MERGE_HEAD?");
 +      other = lookup_commit_or_die(sha1, "MERGE_HEAD");
        add_pending_object(revs, &head->object, "HEAD");
        add_pending_object(revs, &other->object, "MERGE_HEAD");
        bases = get_merge_bases(head, other, 1);
        revs->limited = 1;
  }
  
 -int handle_revision_arg(const char *arg, struct rev_info *revs,
 +int handle_revision_arg(const char *arg_, struct rev_info *revs,
                        int flags,
                        int cant_be_filename)
  {
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
 +      const char *arg = arg_;
  
        dotdot = strstr(arg, "..");
        if (dotdot) {
                const char *this = arg;
                int symmetric = *next == '.';
                unsigned int flags_exclude = flags ^ UNINTERESTING;
 +              unsigned int a_flags;
  
                *dotdot = 0;
                next += symmetric;
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
 -                              a->object.flags |= flags | SYMMETRIC_LEFT;
 +                              a_flags = flags | SYMMETRIC_LEFT;
                        } else
 -                              a->object.flags |= flags_exclude;
 +                              a_flags = flags_exclude;
 +                      a->object.flags |= a_flags;
                        b->object.flags |= flags;
 +                      add_rev_cmdline(revs, &a->object, this,
 +                                      REV_CMD_LEFT, a_flags);
 +                      add_rev_cmdline(revs, &b->object, next,
 +                                      REV_CMD_RIGHT, flags);
                        add_pending_object(revs, &a->object, this);
                        add_pending_object(revs, &b->object, next);
                        return 0;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
 +      add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
        add_pending_object_with_mode(revs, object, arg, mode);
        return 0;
  }
@@@ -1418,11 -1347,6 +1425,11 @@@ static int handle_revision_opt(struct r
                revs->tree_objects = 1;
                revs->blob_objects = 1;
                revs->edge_hint = 1;
 +      } else if (!strcmp(arg, "--verify-objects")) {
 +              revs->tag_objects = 1;
 +              revs->tree_objects = 1;
 +              revs->blob_objects = 1;
 +              revs->verify_objects = 1;
        } else if (!strcmp(arg, "--unpacked")) {
                revs->unpacked = 1;
        } else if (!prefixcmp(arg, "--unpacked=")) {
@@@ -2050,7 -1974,8 +2057,8 @@@ int prepare_revision_walk(struct rev_in
                }
                e++;
        }
-       free(list);
+       if (!revs->leak_pending)
+               free(list);
  
        if (revs->no_walk)
                return 0;
diff --combined revision.h
index 754f31b1cda81c474f71ab56f8d82e260adee968,366a9b49b0f43ee69fc43267a8f58fba19c7ae36..6aa53d1aa708918e4bbbebb042e0bf75c1629b05
@@@ -24,23 -24,6 +24,23 @@@ struct rev_info
  struct log_info;
  struct string_list;
  
 +struct rev_cmdline_info {
 +      unsigned int nr;
 +      unsigned int alloc;
 +      struct rev_cmdline_entry {
 +              struct object *item;
 +              const char *name;
 +              enum {
 +                      REV_CMD_REF,
 +                      REV_CMD_PARENTS_ONLY,
 +                      REV_CMD_LEFT,
 +                      REV_CMD_RIGHT,
 +                      REV_CMD_REV
 +              } whence;
 +              unsigned flags;
 +      } *rev;
 +};
 +
  struct rev_info {
        /* Starting list */
        struct commit_list *commits;
@@@ -49,9 -32,6 +49,9 @@@
        /* Parents of shown commits */
        struct object_array boundary_commits;
  
 +      /* The end-points specified by the end user */
 +      struct rev_cmdline_info cmdline;
 +
        /* Basic information */
        const char *prefix;
        const char *def;
@@@ -73,7 -53,6 +73,7 @@@
                        tag_objects:1,
                        tree_objects:1,
                        blob_objects:1,
 +                      verify_objects:1,
                        edge_hint:1,
                        limited:1,
                        unpacked:1,
                        date_mode_explicit:1,
                        preserve_subject:1;
        unsigned int    disable_stdin:1;
+       unsigned int    leak_pending:1;
  
        enum date_mode date_mode;
  
@@@ -206,14 -186,13 +207,15 @@@ struct name_path 
  
  char *path_name(const struct name_path *path, const char *name);
  
 +extern void show_object_with_name(FILE *, struct object *, const struct name_path *, const char *);
 +
  extern void add_object(struct object *obj,
                       struct object_array *p,
                       struct name_path *path,
                       const char *name);
  
  extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
+ extern void add_pending_sha1(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags);
  
  extern void add_head_to_pending(struct rev_info *);