Merge branch 'nd/maint-autofix-tag-in-head' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 21 Oct 2011 17:49:26 +0000 (10:49 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 21 Oct 2011 17:49:26 +0000 (10:49 -0700)
* nd/maint-autofix-tag-in-head:
Accept tags in HEAD or MERGE_HEAD
merge: remove global variable head[]
merge: use return value of resolve_ref() to determine if HEAD is invalid
merge: keep stash[] a local variable

Conflicts:
builtin/merge.c

1  2 
builtin/commit.c
builtin/merge.c
commit.c
commit.h
http-push.c
revision.c
diff --combined builtin/commit.c
index cbc9613ec661bc2cef8274cd66efb06b9cab55b6,402eb5af53fbd8f4434e72d13f0be8330e667c47..66ffe31736933d1ad15fdeb7bb321f10fcab1dd3
@@@ -254,10 -254,8 +254,10 @@@ static int list_paths(struct string_lis
                ;
        m = xcalloc(1, i);
  
 -      if (with_tree)
 -              overlay_tree_on_cache(with_tree, prefix);
 +      if (with_tree) {
 +              const char *max_prefix = pathspec_prefix(prefix, pattern);
 +              overlay_tree_on_cache(with_tree, max_prefix);
 +      }
  
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
                        item->util = item; /* better a valid pointer than a fake one */
        }
  
 -      return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
 +      return report_path_error(m, pattern, prefix);
  }
  
  static void add_remove_files(struct string_list *list)
@@@ -1141,7 -1139,7 +1141,7 @@@ static int git_status_config(const cha
                return 0;
        }
        if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
 -              s->use_color = git_config_colorbool(k, v, -1);
 +              s->use_color = git_config_colorbool(k, v);
                return 0;
        }
        if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
@@@ -1234,6 -1232,10 +1234,6 @@@ int cmd_status(int argc, const char **a
  
        if (s.relative_paths)
                s.prefix = prefix;
 -      if (s.use_color == -1)
 -              s.use_color = git_use_color_default;
 -      if (diff_use_color_default == -1)
 -              diff_use_color_default = git_use_color_default;
  
        switch (status_format) {
        case STATUS_FORMAT_SHORT:
@@@ -1389,17 -1391,22 +1389,17 @@@ int cmd_commit(int argc, const char **a
        git_config(git_commit_config, &s);
        determine_whence(&s);
  
 -      if (s.use_color == -1)
 -              s.use_color = git_use_color_default;
        if (get_sha1("HEAD", sha1))
                current_head = NULL;
        else {
-               current_head = lookup_commit(sha1);
+               current_head = lookup_commit_or_die(sha1, "HEAD");
                if (!current_head || parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
        argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
                                          prefix, current_head, &s);
 -      if (dry_run) {
 -              if (diff_use_color_default == -1)
 -                      diff_use_color_default = git_use_color_default;
 +      if (dry_run)
                return dry_run_commit(argc, argv, prefix, current_head, &s);
 -      }
        index_file = prepare_index(argc, argv, prefix, current_head, 0);
  
        /* Set up everything for writing the commit object.  This includes
                        pptr = &commit_list_insert(c->item, pptr)->next;
        } else if (whence == FROM_MERGE) {
                struct strbuf m = STRBUF_INIT;
+               struct commit *commit;
                FILE *fp;
  
                if (!reflog_msg)
                        unsigned char sha1[20];
                        if (get_sha1_hex(m.buf, sha1) < 0)
                                die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
-                       pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next;
+                       commit = lookup_commit_or_die(sha1, "MERGE_HEAD");
+                       pptr = &commit_list_insert(commit, pptr)->next;
                }
                fclose(fp);
                strbuf_release(&m);
diff --combined builtin/merge.c
index ab4077f272919fb47e3c5f179dc42fc9baad68bb,9567d60ba2d46e5f30cb6d954a74db8bcb5e9ee5..581f494aee01f97bb87a94dbfd1fba4d14afaa69
@@@ -50,7 -50,6 +50,6 @@@ static int fast_forward_only
  static int allow_trivial = 1, have_message;
  static struct strbuf merge_msg;
  static struct commit_list *remoteheads;
- static unsigned char head[20], stash[20];
  static struct strategy **use_strategies;
  static size_t use_strategies_nr, use_strategies_alloc;
  static const char **xopts;
@@@ -217,7 -216,7 +216,7 @@@ static void drop_save(void
        unlink(git_path("MERGE_MODE"));
  }
  
- static void save_state(void)
+ static int save_state(unsigned char *stash)
  {
        int len;
        struct child_process cp;
  
        if (finish_command(&cp) || len < 0)
                die(_("stash failed"));
-       else if (!len)
-               return;
+       else if (!len)          /* no changes */
+               return -1;
        strbuf_setlen(&buffer, buffer.len-1);
        if (get_sha1(buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
+       return 0;
  }
  
  static void read_empty(unsigned const char *sha1, int verbose)
@@@ -278,7 -278,8 +278,8 @@@ static void reset_hard(unsigned const c
                die(_("read-tree failed"));
  }
  
- static void restore_state(void)
+ static void restore_state(const unsigned char *head,
+                         const unsigned char *stash)
  {
        struct strbuf sb = STRBUF_INIT;
        const char *args[] = { "stash", "apply", NULL, NULL };
@@@ -308,10 -309,9 +309,9 @@@ static void finish_up_to_date(const cha
        drop_save();
  }
  
- static void squash_message(void)
+ static void squash_message(struct commit *commit)
  {
        struct rev_info rev;
-       struct commit *commit;
        struct strbuf out = STRBUF_INIT;
        struct commit_list *j;
        int fd;
        rev.ignore_merges = 1;
        rev.commit_format = CMIT_FMT_MEDIUM;
  
-       commit = lookup_commit(head);
        commit->object.flags |= UNINTERESTING;
        add_pending_object(&rev, &commit->object, NULL);
  
        strbuf_release(&out);
  }
  
- static void finish(const unsigned char *new_head, const char *msg)
+ static void finish(struct commit *head_commit,
+                  const unsigned char *new_head, const char *msg)
  {
        struct strbuf reflog_message = STRBUF_INIT;
+       const unsigned char *head = head_commit->object.sha1;
  
        if (!msg)
                strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
                        getenv("GIT_REFLOG_ACTION"), msg);
        }
        if (squash) {
-               squash_message();
+               squash_message(head_commit);
        } else {
                if (verbosity >= 0 && !merge_msg.len)
                        printf(_("No merge message -- not updating HEAD\n"));
                opts.output_format |=
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
 -              if (diff_use_color_default > 0)
 -                      DIFF_OPT_SET(&opts, COLOR_DIFF);
                if (diff_setup_done(&opts) < 0)
                        die(_("diff_setup_done failed"));
                diff_tree_sha1(head, new_head, "", &opts);
@@@ -661,7 -664,7 +662,7 @@@ int try_merge_command(const char *strat
  }
  
  static int try_merge_strategy(const char *strategy, struct commit_list *common,
-                             const char *head_arg)
+                             struct commit *head, const char *head_arg)
  {
        int index_fd;
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
                        commit_list_insert(j->item, &reversed);
  
                index_fd = hold_locked_index(lock, 1);
-               clean = merge_recursive(&o, lookup_commit(head),
+               clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (active_cache_changed &&
                                (write_cache(index_fd, active_cache, active_nr) ||
@@@ -858,25 -861,26 +859,26 @@@ static void run_prepare_commit_msg(void
        read_merge_msg();
  }
  
- static int merge_trivial(void)
+ static int merge_trivial(struct commit *head)
  {
        unsigned char result_tree[20], result_commit[20];
        struct commit_list *parent = xmalloc(sizeof(*parent));
  
        write_tree_trivial(result_tree);
        printf(_("Wonderful.\n"));
-       parent->item = lookup_commit(head);
+       parent->item = head;
        parent->next = xmalloc(sizeof(*parent->next));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
        run_prepare_commit_msg();
        commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
-       finish(result_commit, "In-index merge");
+       finish(head, result_commit, "In-index merge");
        drop_save();
        return 0;
  }
  
- static int finish_automerge(struct commit_list *common,
+ static int finish_automerge(struct commit *head,
+                           struct commit_list *common,
                            unsigned char *result_tree,
                            const char *wt_strategy)
  {
        free_commit_list(common);
        if (allow_fast_forward) {
                parents = remoteheads;
-               commit_list_insert(lookup_commit(head), &parents);
+               commit_list_insert(head, &parents);
                parents = reduce_heads(parents);
        } else {
                struct commit_list **pptr = &parents;
  
-               pptr = &commit_list_insert(lookup_commit(head),
+               pptr = &commit_list_insert(head,
                                pptr)->next;
                for (j = remoteheads; j; j = j->next)
                        pptr = &commit_list_insert(j->item, pptr)->next;
        strbuf_addch(&merge_msg, '\n');
        run_prepare_commit_msg();
        commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
 -      strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
 +      strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
-       finish(result_commit, buf.buf);
+       finish(head, result_commit, buf.buf);
        strbuf_release(&buf);
        drop_save();
        return 0;
@@@ -936,7 -940,8 +938,8 @@@ static int suggest_conflicts(int renorm
        return 1;
  }
  
- static struct commit *is_old_style_invocation(int argc, const char **argv)
+ static struct commit *is_old_style_invocation(int argc, const char **argv,
+                                             const unsigned char *head)
  {
        struct commit *second_token = NULL;
        if (argc > 2) {
@@@ -1008,9 -1013,12 +1011,12 @@@ static int setup_with_upstream(const ch
  int cmd_merge(int argc, const char **argv, const char *prefix)
  {
        unsigned char result_tree[20];
+       unsigned char stash[20];
+       unsigned char head_sha1[20];
+       struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
-       int flag, head_invalid = 0, i;
+       int flag, i;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
        const char *best_strategy = NULL, *wt_strategy = NULL;
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
         */
-       branch = resolve_ref("HEAD", head, 0, &flag);
+       branch = resolve_ref("HEAD", head_sha1, 0, &flag);
        if (branch && !prefixcmp(branch, "refs/heads/"))
                branch += 11;
-       if (is_null_sha1(head))
-               head_invalid = 1;
+       if (!branch || is_null_sha1(head_sha1))
+               head_commit = NULL;
+       else
+               head_commit = lookup_commit_or_die(head_sha1, "HEAD");
  
        git_config(git_merge_config, NULL);
  
 -      /* for color.ui */
 -      if (diff_use_color_default == -1)
 -              diff_use_color_default = git_use_color_default;
 -
        if (branch_mergeoptions)
                parse_branch_merge_options(branch_mergeoptions);
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
         * additional safety measure to check for it.
         */
  
-       if (!have_message && is_old_style_invocation(argc, argv)) {
+       if (!have_message && head_commit &&
+           is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
                strbuf_addstr(&merge_msg, argv[0]);
                head_arg = argv[1];
                argv += 2;
                argc -= 2;
-       } else if (head_invalid) {
+       } else if (!head_commit) {
                struct object *remote_head;
                /*
                 * If the merged head is a valid one there is no reason
                }
        }
  
-       if (head_invalid || !argc)
+       if (!head_commit || !argc)
                usage_with_options(builtin_merge_usage,
                        builtin_merge_options);
  
        }
  
        if (!remoteheads->next)
-               common = get_merge_bases(lookup_commit(head),
-                               remoteheads->item, 1);
+               common = get_merge_bases(head_commit, remoteheads->item, 1);
        else {
                struct commit_list *list = remoteheads;
-               commit_list_insert(lookup_commit(head), &list);
+               commit_list_insert(head_commit, &list);
                common = get_octopus_merge_bases(list);
                free(list);
        }
  
-       update_ref("updating ORIG_HEAD", "ORIG_HEAD", head, NULL, 0,
-               DIE_ON_ERR);
+       update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
+                  NULL, 0, DIE_ON_ERR);
  
        if (!common)
                ; /* No common ancestors found. We need a real merge. */
                return 0;
        } else if (allow_fast_forward && !remoteheads->next &&
                        !common->next &&
-                       !hashcmp(common->item->object.sha1, head)) {
+                       !hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
                /* Again the most common case of merging one remote. */
                struct strbuf msg = STRBUF_INIT;
                struct object *o;
                char hex[41];
  
-               strcpy(hex, find_unique_abbrev(head, DEFAULT_ABBREV));
+               strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
  
                if (verbosity >= 0)
                        printf(_("Updating %s..%s\n"),
                if (!o)
                        return 1;
  
-               if (checkout_fast_forward(head, remoteheads->item->object.sha1))
+               if (checkout_fast_forward(head_commit->object.sha1, remoteheads->item->object.sha1))
                        return 1;
  
-               finish(o->sha1, msg.buf);
+               finish(head_commit, o->sha1, msg.buf);
                drop_save();
                return 0;
        } else if (!remoteheads->next && common->next)
                        git_committer_info(IDENT_ERROR_ON_NO_NAME);
                        printf(_("Trying really trivial in-index merge...\n"));
                        if (!read_tree_trivial(common->item->object.sha1,
-                                       head, remoteheads->item->object.sha1))
-                               return merge_trivial();
+                                       head_commit->object.sha1, remoteheads->item->object.sha1))
+                               return merge_trivial(head_commit);
                        printf(_("Nope.\n"));
                }
        } else {
                         * merge_bases again, otherwise "git merge HEAD^
                         * HEAD^^" would be missed.
                         */
-                       common_one = get_merge_bases(lookup_commit(head),
-                               j->item, 1);
+                       common_one = get_merge_bases(head_commit, j->item, 1);
                        if (hashcmp(common_one->item->object.sha1,
                                j->item->object.sha1)) {
                                up_to_date = 0;
         * sync with the head commit.  The strategies are responsible
         * to ensure this.
         */
-       if (use_strategies_nr != 1) {
-               /*
-                * Stash away the local changes so that we can try more
-                * than one.
-                */
-               save_state();
-       } else {
-               memcpy(stash, null_sha1, 20);
-       }
+       if (use_strategies_nr == 1 ||
+           /*
+            * Stash away the local changes so that we can try more than one.
+            */
+           save_state(stash))
+               hashcpy(stash, null_sha1);
  
        for (i = 0; i < use_strategies_nr; i++) {
                int ret;
                if (i) {
                        printf(_("Rewinding the tree to pristine...\n"));
-                       restore_state();
+                       restore_state(head_commit->object.sha1, stash);
                }
                if (use_strategies_nr != 1)
                        printf(_("Trying merge strategy %s...\n"),
                wt_strategy = use_strategies[i]->name;
  
                ret = try_merge_strategy(use_strategies[i]->name,
-                       common, head_arg);
+                                        common, head_commit, head_arg);
                if (!option_commit && !ret) {
                        merge_was_ok = 1;
                        /*
         * auto resolved the merge cleanly.
         */
        if (automerge_was_ok)
-               return finish_automerge(common, result_tree, wt_strategy);
+               return finish_automerge(head_commit, common, result_tree,
+                                       wt_strategy);
  
        /*
         * Pick the result from the best strategy and have the user fix
         * it up.
         */
        if (!best_strategy) {
-               restore_state();
+               restore_state(head_commit->object.sha1, stash);
                if (use_strategies_nr > 1)
                        fprintf(stderr,
                                _("No merge strategy handled the merge.\n"));
                ; /* We already have its result in the working tree. */
        else {
                printf(_("Rewinding the tree to pristine...\n"));
-               restore_state();
+               restore_state(head_commit->object.sha1, stash);
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
-               try_merge_strategy(best_strategy, common, head_arg);
+               try_merge_strategy(best_strategy, common, head_commit, head_arg);
        }
  
        if (squash)
-               finish(NULL, NULL);
+               finish(head_commit, NULL, NULL);
        else {
                int fd;
                struct commit_list *j;
diff --combined commit.c
index 97b43279cdf46159462d5f56abc20f2161f4c7ea,50fcf96c243c975a9c1be8443e84c7c4517cdc5b..9f4cc636dd74e99a1311500e15501bdb4875cb9d
+++ b/commit.c
@@@ -39,6 -39,18 +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);
@@@ -214,12 -226,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)
@@@ -505,7 -527,7 +517,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 12d100b8b6fcd092f3a6886a75c720011ef1b7dc,190c1d6883ad022cf90ae4f3dc21d5f939463181..14f6a5a2ed91e2176cce4543798774f88a8628b1
+++ b/commit.h
@@@ -38,6 -38,13 +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);
  
@@@ -142,7 -149,6 +149,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);
@@@ -154,7 -160,7 +161,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 http-push.c
index 376331a76fcde24e6828eba0b88fec26d93fe56f,d432b30379d3a91d9f5fb66d0768632688f86fac..44f814cda2a9756a55cb7f332d5fef0e5484256b
@@@ -377,15 -377,15 +377,15 @@@ static void start_put(struct transfer_r
        unsigned long len;
        int hdrlen;
        ssize_t size;
 -      z_stream stream;
 +      git_zstream stream;
  
        unpacked = read_sha1_file(request->obj->sha1, &type, &len);
        hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
  
        /* Set it up */
        memset(&stream, 0, sizeof(stream));
 -      deflateInit(&stream, zlib_compression_level);
 -      size = deflateBound(&stream, len + hdrlen);
 +      git_deflate_init(&stream, zlib_compression_level);
 +      size = git_deflate_bound(&stream, len + hdrlen);
        strbuf_init(&request->buffer.buf, size);
        request->buffer.posn = 0;
  
        /* First header.. */
        stream.next_in = (void *)hdr;
        stream.avail_in = hdrlen;
 -      while (deflate(&stream, 0) == Z_OK)
 -              /* nothing */;
 +      while (git_deflate(&stream, 0) == Z_OK)
 +              ; /* nothing */
  
        /* Then the data itself.. */
        stream.next_in = unpacked;
        stream.avail_in = len;
 -      while (deflate(&stream, Z_FINISH) == Z_OK)
 -              /* nothing */;
 -      deflateEnd(&stream);
 +      while (git_deflate(&stream, Z_FINISH) == Z_OK)
 +              ; /* nothing */
 +      git_deflate_end(&stream);
        free(unpacked);
  
        request->buffer.buf.len = stream.total_out;
@@@ -1606,10 -1606,10 +1606,10 @@@ static void fetch_symref(const char *pa
        strbuf_release(&buffer);
  }
  
- static int verify_merge_base(unsigned char *head_sha1, unsigned char *branch_sha1)
+ static int verify_merge_base(unsigned char *head_sha1, struct ref *remote)
  {
-       struct commit *head = lookup_commit(head_sha1);
-       struct commit *branch = lookup_commit(branch_sha1);
+       struct commit *head = lookup_commit_or_die(head_sha1, "HEAD");
+       struct commit *branch = lookup_commit_or_die(remote->old_sha1, remote->name);
        struct commit_list *merge_bases = get_merge_bases(head, branch, 1);
  
        return (merge_bases && !merge_bases->next && merge_bases->item == branch);
@@@ -1655,7 -1655,7 +1655,7 @@@ static int delete_remote_branch(const c
                return error("Remote HEAD is not a symref");
  
        /* Remote branch must not be the remote HEAD */
 -      for (i=0; symref && i<MAXDEPTH; i++) {
 +      for (i = 0; symref && i < MAXDEPTH; i++) {
                if (!strcmp(remote_ref->name, symref))
                        return error("Remote branch %s is the current HEAD",
                                     remote_ref->name);
                        return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, sha1_to_hex(remote_ref->old_sha1));
  
                /* Remote branch must be an ancestor of remote HEAD */
-               if (!verify_merge_base(head_sha1, remote_ref->old_sha1)) {
+               if (!verify_merge_base(head_sha1, remote_ref)) {
                        return error("The branch '%s' is not an ancestor "
                                     "of your current HEAD.\n"
                                     "If you are sure you want to delete it,"
diff --combined revision.c
index 102456b009429eeecdee3a5f8f9a1a0404f1cffa,5e057a0aedbc3106c0bd2199167f88bc56964e91..3d2deedaf8e50c6a71b9750dacf602bb5b9238b9
@@@ -729,16 -729,12 +729,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;
  }
  
@@@ -769,7 -765,7 +769,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;
@@@ -855,7 -834,6 +855,7 @@@ static int handle_one_ref(const char *p
        struct all_refs_cb *cb = cb_data;
        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_object(cb->all_revs, object, path);
        return 0;
  }
@@@ -882,7 -860,6 +882,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) {
@@@ -919,13 -896,12 +919,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;
@@@ -1011,10 -986,12 +1011,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;
  }