Merge branch 'nd/resolve-ref'
authorJunio C Hamano <gitster@pobox.com>
Fri, 9 Dec 2011 21:37:14 +0000 (13:37 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 9 Dec 2011 21:37:14 +0000 (13:37 -0800)
* nd/resolve-ref:
Copy resolve_ref() return value for longer use
Convert many resolve_ref() calls to read_ref*() and ref_exists()

Conflicts:
builtin/fmt-merge-msg.c
builtin/merge.c
refs.c

1  2 
builtin/branch.c
builtin/checkout.c
builtin/commit.c
builtin/fmt-merge-msg.c
builtin/merge.c
builtin/tag.c
cache.h
notes-merge.c
refs.c
diff --combined builtin/branch.c
index ad02f0fd7e8819f436baf27d7836cc181d613bab,3ef15f7fc5210f30271716378bf6cb8fd6252f1b..e1e486e4c51194e09df3779be254cb72855d1103
@@@ -115,8 -115,10 +115,10 @@@ static int branch_merged(int kind, cons
                    branch->merge[0] &&
                    branch->merge[0]->dst &&
                    (reference_name =
-                    resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+                    resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL) {
+                       reference_name = xstrdup(reference_name);
                        reference_rev = lookup_commit_reference(sha1);
+               }
        }
        if (!reference_rev)
                reference_rev = head_rev;
                                "         '%s', even though it is merged to HEAD."),
                                name, reference_name);
        }
+       free((char *)reference_name);
        return merged;
  }
  
@@@ -186,7 -189,7 +189,7 @@@ static int delete_branches(int argc, co
                free(name);
  
                name = xstrdup(mkpath(fmt, bname.buf));
-               if (!resolve_ref(name, sha1, 1, NULL)) {
+               if (read_ref(name, sha1)) {
                        error(_("%sbranch '%s' not found."),
                                        remote, bname.buf);
                        ret = 1;
@@@ -565,7 -568,6 +568,6 @@@ static int print_ref_list(int kinds, in
  static void rename_branch(const char *oldname, const char *newname, int force)
  {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
-       unsigned char sha1[20];
        struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
        int recovery = 0;
  
                 * Bad name --- this could be an attempt to rename a
                 * ref that we used to allow to be created by accident.
                 */
-               if (resolve_ref(oldref.buf, sha1, 1, NULL))
+               if (ref_exists(oldref.buf))
                        recovery = 1;
                else
                        die(_("Invalid branch name: '%s'"), oldname);
@@@ -623,49 -625,11 +625,49 @@@ static int opt_parse_merge_filter(cons
        return 0;
  }
  
 +static const char edit_description[] = "BRANCH_DESCRIPTION";
 +
 +static int edit_branch_description(const char *branch_name)
 +{
 +      FILE *fp;
 +      int status;
 +      struct strbuf buf = STRBUF_INIT;
 +      struct strbuf name = STRBUF_INIT;
 +
 +      read_branch_desc(&buf, branch_name);
 +      if (!buf.len || buf.buf[buf.len-1] != '\n')
 +              strbuf_addch(&buf, '\n');
 +      strbuf_addf(&buf,
 +                  "# Please edit the description for the branch\n"
 +                  "#   %s\n"
 +                  "# Lines starting with '#' will be stripped.\n",
 +                  branch_name);
 +      fp = fopen(git_path(edit_description), "w");
 +      if ((fwrite(buf.buf, 1, buf.len, fp) < buf.len) || fclose(fp)) {
 +              strbuf_release(&buf);
 +              return error(_("could not write branch description template: %s\n"),
 +                           strerror(errno));
 +      }
 +      strbuf_reset(&buf);
 +      if (launch_editor(git_path(edit_description), &buf, NULL)) {
 +              strbuf_release(&buf);
 +              return -1;
 +      }
 +      stripspace(&buf, 1);
 +
 +      strbuf_addf(&name, "branch.%s.description", branch_name);
 +      status = git_config_set(name.buf, buf.buf);
 +      strbuf_release(&name);
 +      strbuf_release(&buf);
 +
 +      return status;
 +}
 +
  int cmd_branch(int argc, const char **argv, const char *prefix)
  {
        int delete = 0, rename = 0, force_create = 0, list = 0;
        int verbose = 0, abbrev = -1, detached = 0;
 -      int reflog = 0;
 +      int reflog = 0, edit_description = 0;
        enum branch_track track;
        int kinds = REF_LOCAL_BRANCH;
        struct commit_list *with_commit = NULL;
                OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
                OPT_BOOLEAN(0, "list", &list, "list branch names"),
                OPT_BOOLEAN('l', "create-reflog", &reflog, "create the branch's reflog"),
 +              OPT_BOOLEAN(0, "edit-description", &edit_description,
 +                          "edit the description for the branch"),
                OPT__FORCE(&force_create, "force creation (when already exists)"),
                {
                        OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
        argc = parse_options(argc, argv, prefix, options, builtin_branch_usage,
                             0);
  
 -      if (!delete && !rename && !force_create && argc == 0)
 +      if (!delete && !rename && !edit_description && argc == 0)
                list = 1;
  
        if (!!delete + !!rename + !!force_create + !!list > 1)
        else if (list)
                return print_ref_list(kinds, detached, verbose, abbrev,
                                      with_commit, argv);
 -      else if (rename) {
 +      else if (edit_description) {
 +              const char *branch_name;
 +              if (detached)
 +                      die("Cannot give description to detached HEAD");
 +              if (!argc)
 +                      branch_name = head;
 +              else if (argc == 1)
 +                      branch_name = argv[0];
 +              else
 +                      usage_with_options(builtin_branch_usage, options);
 +              if (edit_branch_description(branch_name))
 +                      return 1;
 +      } else if (rename) {
                if (argc == 1)
                        rename_branch(head, argv[0], rename > 1);
                else if (argc == 2)
                        rename_branch(argv[0], argv[1], rename > 1);
                else
                        usage_with_options(builtin_branch_usage, options);
 -      } else if (argc <= 2) {
 +      } else if (argc > 0 && argc <= 2) {
                if (kinds != REF_LOCAL_BRANCH)
                        die(_("-a and -r options to 'git branch' do not make sense with a branch name"));
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
diff --combined builtin/checkout.c
index 51840b9784f0daec21087ec101304eaa6cbf73cd,c6919f16871a97b4f91fe525e4b8e89f43eecd58..b7c630287dda662630af9883655ed23bd50e7b7e
@@@ -288,7 -288,7 +288,7 @@@ static int checkout_paths(struct tree *
            commit_locked_index(lock_file))
                die(_("unable to write new index file"));
  
-       resolve_ref("HEAD", rev, 0, &flag);
+       read_ref_full("HEAD", rev, 0, &flag);
        head = lookup_commit_reference_gently(rev, 1);
  
        errs |= post_checkout_hook(head, head, 0);
@@@ -411,7 -411,7 +411,7 @@@ static int merge_working_tree(struct ch
                topts.fn = twoway_merge;
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
 -              topts.dir->exclude_per_dir = ".gitignore";
 +              setup_standard_excludes(topts.dir);
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           EMPTY_TREE_SHA1_BIN);
@@@ -699,7 -699,9 +699,9 @@@ static int switch_branches(struct check
        unsigned char rev[20];
        int flag;
        memset(&old, 0, sizeof(old));
-       old.path = xstrdup(resolve_ref("HEAD", rev, 0, &flag));
+       old.path = resolve_ref("HEAD", rev, 0, &flag);
+       if (old.path)
+               old.path = xstrdup(old.path);
        old.commit = lookup_commit_reference_gently(rev, 1);
        if (!(flag & REF_ISSYMREF)) {
                free((char *)old.path);
@@@ -866,7 -868,7 +868,7 @@@ static int parse_branchname_arg(int arg
        setup_branch_path(new);
  
        if (!check_refname_format(new->path, 0) &&
-           resolve_ref(new->path, branch_rev, 1, NULL))
+           !read_ref(new->path, branch_rev))
                hashcpy(rev, branch_rev);
        else
                new->path = NULL; /* not an existing branch */
diff --combined builtin/commit.c
index cf1447204fbec4979972130432b50107524eac39,f3a6ed2bf5b9ff312dd881e6d44bfcd71fb0b73b..e36e9adf87d76002784eb26107c25e5d91683229
@@@ -1259,7 -1259,7 +1259,7 @@@ static void print_summary(const char *p
        struct commit *commit;
        struct strbuf format = STRBUF_INIT;
        unsigned char junk_sha1[20];
-       const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
+       const char *head;
        struct pretty_print_context pctx = {0};
        struct strbuf author_ident = STRBUF_INIT;
        struct strbuf committer_ident = STRBUF_INIT;
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
  
+       head = resolve_ref("HEAD", junk_sha1, 0, NULL);
        printf("[%s%s ",
                !prefixcmp(head, "refs/heads/") ?
                        head + 11 :
@@@ -1382,7 -1383,6 +1383,7 @@@ int cmd_commit(int argc, const char **a
        int allow_fast_forward = 1;
        struct wt_status s;
        struct commit *current_head = NULL;
 +      struct commit_extra_header *extra = NULL;
  
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_commit_usage, builtin_commit_options);
                        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)
                        die_errno(_("could not open '%s' for reading"),
                                  git_path("MERGE_HEAD"));
                while (strbuf_getline(&m, fp, '\n') != EOF) {
 -                      unsigned char sha1[20];
 -                      if (get_sha1_hex(m.buf, sha1) < 0)
 +                      struct commit *parent;
 +
 +                      parent = get_merge_parent(m.buf);
 +                      if (!parent)
                                die(_("Corrupt MERGE_HEAD file (%s)"), m.buf);
 -                      commit = lookup_commit_or_die(sha1, "MERGE_HEAD");
 -                      pptr = &commit_list_insert(commit, pptr)->next;
 +                      pptr = &commit_list_insert(parent, pptr)->next;
                }
                fclose(fp);
                strbuf_release(&m);
                exit(1);
        }
  
 -      if (commit_tree(sb.buf, active_cache_tree->sha1, parents, sha1,
 -                      author_ident.buf)) {
 +      if (amend)
 +              extra = read_commit_extra_headers(current_head);
 +
 +      if (commit_tree_extended(sb.buf, active_cache_tree->sha1, parents, sha1,
 +                               author_ident.buf, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
        strbuf_release(&author_ident);
 +      free_commit_extra_headers(extra);
  
        ref_lock = lock_any_ref_for_update("HEAD",
                                           !current_head
        }
  
        unlink(git_path("CHERRY_PICK_HEAD"));
 +      unlink(git_path("REVERT_HEAD"));
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
        unlink(git_path("MERGE_MODE"));
diff --combined builtin/fmt-merge-msg.c
index ed95349b424f2f3d2aa671a277b346ae6198d34c,a3ba21520916ce2e244839304def0a748e8a53c7..bdfa0ea05d99089937b9e753cf85a8489a5e27cb
@@@ -5,43 -5,32 +5,43 @@@
  #include "revision.h"
  #include "tag.h"
  #include "string-list.h"
 +#include "branch.h"
 +#include "fmt-merge-msg.h"
 +#include "gpg-interface.h"
  
  static const char * const fmt_merge_msg_usage[] = {
        "git fmt-merge-msg [-m <message>] [--log[=<n>]|--no-log] [--file <file>]",
        NULL
  };
  
 -static int shortlog_len;
 +static int use_branch_desc;
  
 -static int fmt_merge_msg_config(const char *key, const char *value, void *cb)
 +int fmt_merge_msg_config(const char *key, const char *value, void *cb)
  {
        if (!strcmp(key, "merge.log") || !strcmp(key, "merge.summary")) {
                int is_bool;
 -              shortlog_len = git_config_bool_or_int(key, value, &is_bool);
 -              if (!is_bool && shortlog_len < 0)
 +              merge_log_config = git_config_bool_or_int(key, value, &is_bool);
 +              if (!is_bool && merge_log_config < 0)
                        return error("%s: negative length %s", key, value);
 -              if (is_bool && shortlog_len)
 -                      shortlog_len = DEFAULT_MERGE_LOG_LEN;
 +              if (is_bool && merge_log_config)
 +                      merge_log_config = DEFAULT_MERGE_LOG_LEN;
 +      } else if (!strcmp(key, "merge.branchdesc")) {
 +              use_branch_desc = git_config_bool(key, value);
        }
        return 0;
  }
  
 +/* merge data per repository where the merged tips came from */
  struct src_data {
        struct string_list branch, tag, r_branch, generic;
        int head_status;
  };
  
 +struct origin_data {
 +      unsigned char sha1[20];
 +      unsigned is_local_branch:1;
 +};
 +
  static void init_src_data(struct src_data *data)
  {
        data->branch.strdup_strings = 1;
@@@ -56,7 -45,7 +56,7 @@@ static struct string_list origins = STR
  static int handle_line(char *line)
  {
        int i, len = strlen(line);
 -      unsigned char *sha1;
 +      struct origin_data *origin_data;
        char *src, *origin;
        struct src_data *src_data;
        struct string_list_item *item;
                return 2;
  
        line[40] = 0;
 -      sha1 = xmalloc(20);
 -      i = get_sha1(line, sha1);
 +      origin_data = xcalloc(1, sizeof(struct origin_data));
 +      i = get_sha1(line, origin_data->sha1);
        line[40] = '\t';
 -      if (i)
 +      if (i) {
 +              free(origin_data);
                return 3;
 +      }
  
        if (line[len - 1] == '\n')
                line[len - 1] = 0;
        line += 42;
  
 +      /*
 +       * At this point, line points at the beginning of comment e.g.
 +       * "branch 'frotz' of git://that/repository.git".
 +       * Find the repository name and point it with src.
 +       */
        src = strstr(line, " of ");
        if (src) {
                *src = 0;
                origin = src;
                src_data->head_status |= 1;
        } else if (!prefixcmp(line, "branch ")) {
 +              origin_data->is_local_branch = 1;
                origin = line + 7;
                string_list_append(&src_data->branch, origin);
                src_data->head_status |= 2;
                sprintf(new_origin, "%s of %s", origin, src);
                origin = new_origin;
        }
 -      string_list_append(&origins, origin)->util = sha1;
 +      if (strcmp(".", src))
 +              origin_data->is_local_branch = 0;
 +      string_list_append(&origins, origin)->util = origin_data;
        return 0;
  }
  
@@@ -161,30 -140,9 +161,30 @@@ static void print_joined(const char *si
        }
  }
  
 -static void shortlog(const char *name, unsigned char *sha1,
 -              struct commit *head, struct rev_info *rev, int limit,
 -              struct strbuf *out)
 +static void add_branch_desc(struct strbuf *out, const char *name)
 +{
 +      struct strbuf desc = STRBUF_INIT;
 +
 +      if (!read_branch_desc(&desc, name)) {
 +              const char *bp = desc.buf;
 +              while (*bp) {
 +                      const char *ep = strchrnul(bp, '\n');
 +                      if (*ep)
 +                              ep++;
 +                      strbuf_addf(out, "  : %.*s", (int)(ep - bp), bp);
 +                      bp = ep;
 +              }
 +              if (out->buf[out->len - 1] != '\n')
 +                      strbuf_addch(out, '\n');
 +      }
 +      strbuf_release(&desc);
 +}
 +
 +static void shortlog(const char *name,
 +                   struct origin_data *origin_data,
 +                   struct commit *head,
 +                   struct rev_info *rev, int limit,
 +                   struct strbuf *out)
  {
        int i, count = 0;
        struct commit *commit;
        struct string_list subjects = STRING_LIST_INIT_DUP;
        int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
        struct strbuf sb = STRBUF_INIT;
 +      const unsigned char *sha1 = origin_data->sha1;
  
        branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
        if (!branch || branch->type != OBJ_COMMIT)
        else
                strbuf_addf(out, "\n* %s:\n", name);
  
 +      if (origin_data->is_local_branch && use_branch_desc)
 +              add_branch_desc(out, name);
 +
        for (i = 0; i < subjects.nr; i++)
                if (i >= limit)
                        strbuf_addf(out, "  ...\n");
        string_list_clear(&subjects, 0);
  }
  
 -static void do_fmt_merge_msg_title(struct strbuf *out,
 +static void fmt_merge_msg_title(struct strbuf *out,
        const char *current_branch) {
        int i = 0;
        char *sep = "";
                strbuf_addf(out, " into %s\n", current_branch);
  }
  
 -static int do_fmt_merge_msg(int merge_title, struct strbuf *in,
 -      struct strbuf *out, int shortlog_len) {
 +static void fmt_tag_signature(struct strbuf *tagbuf,
 +                            struct strbuf *sig,
 +                            const char *buf,
 +                            unsigned long len)
 +{
 +      const char *tag_body = strstr(buf, "\n\n");
 +      if (tag_body) {
 +              tag_body += 2;
 +              strbuf_add(tagbuf, tag_body, buf + len - tag_body);
 +      }
 +      strbuf_complete_line(tagbuf);
 +      strbuf_add_lines(tagbuf, "# ", sig->buf, sig->len);
 +}
 +
 +static void fmt_merge_msg_sigs(struct strbuf *out)
 +{
 +      int i, tag_number = 0, first_tag = 0;
 +      struct strbuf tagbuf = STRBUF_INIT;
 +
 +      for (i = 0; i < origins.nr; i++) {
 +              unsigned char *sha1 = origins.items[i].util;
 +              enum object_type type;
 +              unsigned long size, len;
 +              char *buf = read_sha1_file(sha1, &type, &size);
 +              struct strbuf sig = STRBUF_INIT;
 +
 +              if (!buf || type != OBJ_TAG)
 +                      goto next;
 +              len = parse_signature(buf, size);
 +
 +              if (size == len)
 +                      ; /* merely annotated */
 +              else if (verify_signed_buffer(buf, len, buf + len, size - len, &sig)) {
 +                      if (!sig.len)
 +                              strbuf_addstr(&sig, "gpg verification failed.\n");
 +              }
 +
 +              if (!tag_number++) {
 +                      fmt_tag_signature(&tagbuf, &sig, buf, len);
 +                      first_tag = i;
 +              } else {
 +                      if (tag_number == 2) {
 +                              struct strbuf tagline = STRBUF_INIT;
 +                              strbuf_addf(&tagline, "\n# %s\n",
 +                                          origins.items[first_tag].string);
 +                              strbuf_insert(&tagbuf, 0, tagline.buf,
 +                                            tagline.len);
 +                              strbuf_release(&tagline);
 +                      }
 +                      strbuf_addf(&tagbuf, "\n# %s\n",
 +                                  origins.items[i].string);
 +                      fmt_tag_signature(&tagbuf, &sig, buf, len);
 +              }
 +              strbuf_release(&sig);
 +      next:
 +              free(buf);
 +      }
 +      if (tagbuf.len) {
 +              strbuf_addch(out, '\n');
 +              strbuf_addbuf(out, &tagbuf);
 +      }
 +      strbuf_release(&tagbuf);
 +}
 +
 +int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 +                struct fmt_merge_msg_opts *opts)
 +{
        int i = 0, pos = 0;
        unsigned char head_sha1[20];
        const char *current_branch;
                die("No current branch");
        if (!prefixcmp(current_branch, "refs/heads/"))
                current_branch += 11;
+       current_branch = xstrdup(current_branch);
  
        /* get a line */
        while (pos < in->len) {
                        die ("Error in line %d: %.*s", i, len, p);
        }
  
 -      if (!srcs.nr) {
 -              free((char*)current_branch);
 -              return 0;
 -      }
 +      if (opts->add_title && srcs.nr)
 +              fmt_merge_msg_title(out, current_branch);
  
 -      if (merge_title)
 -              do_fmt_merge_msg_title(out, current_branch);
 +      if (origins.nr)
 +              fmt_merge_msg_sigs(out);
  
 -      if (shortlog_len) {
 +      if (opts->shortlog_len) {
                struct commit *head;
                struct rev_info rev;
  
                        strbuf_addch(out, '\n');
  
                for (i = 0; i < origins.nr; i++)
 -                      shortlog(origins.items[i].string, origins.items[i].util,
 -                                      head, &rev, shortlog_len, out);
 +                      shortlog(origins.items[i].string,
 +                               origins.items[i].util,
 +                               head, &rev, opts->shortlog_len, out);
        }
 +
 +      strbuf_complete_line(out);
+       free((char *)current_branch);
        return 0;
  }
  
 -int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 -                int merge_title, int shortlog_len) {
 -      return do_fmt_merge_msg(merge_title, in, out, shortlog_len);
 -}
 -
  int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
  {
        const char *inpath = NULL;
        const char *message = NULL;
 +      int shortlog_len = -1;
        struct option options[] = {
                { OPTION_INTEGER, 0, "log", &shortlog_len, "n",
                  "populate log with at most <n> entries from shortlog",
        FILE *in = stdin;
        struct strbuf input = STRBUF_INIT, output = STRBUF_INIT;
        int ret;
 +      struct fmt_merge_msg_opts opts;
  
        git_config(fmt_merge_msg_config, NULL);
        argc = parse_options(argc, argv, prefix, options, fmt_merge_msg_usage,
                             0);
        if (argc > 0)
                usage_with_options(fmt_merge_msg_usage, options);
 -      if (message && !shortlog_len) {
 -              char nl = '\n';
 -              write_in_full(STDOUT_FILENO, message, strlen(message));
 -              write_in_full(STDOUT_FILENO, &nl, 1);
 -              return 0;
 -      }
        if (shortlog_len < 0)
 -              die("Negative --log=%d", shortlog_len);
 +              shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
  
        if (inpath && strcmp(inpath, "-")) {
                in = fopen(inpath, "r");
  
        if (message)
                strbuf_addstr(&output, message);
 -      ret = fmt_merge_msg(&input, &output,
 -                          message ? 0 : 1,
 -                          shortlog_len);
  
 +      memset(&opts, 0, sizeof(opts));
 +      opts.add_title = !message;
 +      opts.shortlog_len = shortlog_len;
 +
 +      ret = fmt_merge_msg(&input, &output, &opts);
        if (ret)
                return ret;
        write_in_full(STDOUT_FILENO, output.buf, output.len);
diff --combined builtin/merge.c
index 7349396d5b44f6ee894e0f0f50e2b66bb31203be,9cda40003e7af5ff55acd2793248f78916ff433d..a1c85344b2b54f957ea69ace864ea99b7d295405
@@@ -26,7 -26,6 +26,7 @@@
  #include "merge-recursive.h"
  #include "resolve-undo.h"
  #include "remote.h"
 +#include "fmt-merge-msg.h"
  
  #define DEFAULT_TWOHEAD (1<<0)
  #define DEFAULT_OCTOPUS (1<<1)
@@@ -45,7 -44,7 +45,7 @@@ static const char * const builtin_merge
        NULL
  };
  
 -static int show_diffstat = 1, shortlog_len, squash;
 +static int show_diffstat = 1, shortlog_len = -1, squash;
  static int option_commit = 1, allow_fast_forward = 1;
  static int fast_forward_only, option_edit;
  static int allow_trivial = 1, have_message;
@@@ -317,15 -316,13 +317,15 @@@ static void squash_message(struct commi
        struct rev_info rev;
        struct strbuf out = STRBUF_INIT;
        struct commit_list *j;
 +      const char *filename;
        int fd;
        struct pretty_print_context ctx = {0};
  
        printf(_("Squash commit -- not updating HEAD\n"));
 -      fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
 +      filename = git_path("SQUASH_MSG");
 +      fd = open(filename, O_WRONLY | O_CREAT, 0666);
        if (fd < 0)
 -              die_errno(_("Could not write to '%s'"), git_path("SQUASH_MSG"));
 +              die_errno(_("Could not write to '%s'"), filename);
  
        init_revisions(&rev, NULL);
        rev.ignore_merges = 1;
@@@ -409,11 -406,21 +409,11 @@@ static void finish(struct commit *head_
        strbuf_release(&reflog_message);
  }
  
 -static struct object *want_commit(const char *name)
 -{
 -      struct object *obj;
 -      unsigned char sha1[20];
 -      if (get_sha1(name, sha1))
 -              return NULL;
 -      obj = parse_object(sha1);
 -      return peel_to_type(name, 0, obj, OBJ_COMMIT);
 -}
 -
  /* Get the name for the merge commit's message. */
  static void merge_name(const char *remote, struct strbuf *msg)
  {
 -      struct object *remote_head;
 +      struct commit *remote_head;
-       unsigned char branch_head[20], buf_sha[20];
+       unsigned char branch_head[20];
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
        const char *ptr;
        remote = bname.buf;
  
        memset(branch_head, 0, sizeof(branch_head));
 -      remote_head = want_commit(remote);
 +      remote_head = get_merge_parent(remote);
        if (!remote_head)
                die(_("'%s' does not point to a commit"), remote);
  
                                    sha1_to_hex(branch_head), remote);
                        goto cleanup;
                }
 +              if (!prefixcmp(found_ref, "refs/tags/")) {
 +                      strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
 +                                  sha1_to_hex(branch_head), remote);
 +                      goto cleanup;
 +              }
                if (!prefixcmp(found_ref, "refs/remotes/")) {
                        strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
                                    sha1_to_hex(branch_head), remote);
                strbuf_addstr(&truname, "refs/heads/");
                strbuf_addstr(&truname, remote);
                strbuf_setlen(&truname, truname.len - len);
-               if (resolve_ref(truname.buf, buf_sha, 1, NULL)) {
+               if (ref_exists(truname.buf)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
 -                                  sha1_to_hex(remote_head->sha1),
 +                                  sha1_to_hex(remote_head->object.sha1),
                                    truname.buf + 11,
                                    (early ? " (early part)" : ""));
                        strbuf_release(&truname);
  
        if (!strcmp(remote, "FETCH_HEAD") &&
                        !access(git_path("FETCH_HEAD"), R_OK)) {
 +              const char *filename;
                FILE *fp;
                struct strbuf line = STRBUF_INIT;
                char *ptr;
  
 -              fp = fopen(git_path("FETCH_HEAD"), "r");
 +              filename = git_path("FETCH_HEAD");
 +              fp = fopen(filename, "r");
                if (!fp)
                        die_errno(_("could not open '%s' for reading"),
 -                                git_path("FETCH_HEAD"));
 +                                filename);
                strbuf_getline(&line, fp, '\n');
                fclose(fp);
                ptr = strstr(line.buf, "\tnot-for-merge\t");
                goto cleanup;
        }
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
 -              sha1_to_hex(remote_head->sha1), remote);
 +              sha1_to_hex(remote_head->object.sha1), remote);
  cleanup:
        strbuf_release(&buf);
        strbuf_release(&bname);
@@@ -538,8 -538,6 +538,8 @@@ static void parse_branch_merge_options(
  
  static int git_merge_config(const char *k, const char *v, void *cb)
  {
 +      int status;
 +
        if (branch && !prefixcmp(k, "branch.") &&
                !prefixcmp(k + 7, branch) &&
                !strcmp(k + 7 + strlen(branch), ".mergeoptions")) {
                return git_config_string(&pull_octopus, k, v);
        else if (!strcmp(k, "merge.renormalize"))
                option_renormalize = git_config_bool(k, v);
 -      else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary")) {
 -              int is_bool;
 -              shortlog_len = git_config_bool_or_int(k, v, &is_bool);
 -              if (!is_bool && shortlog_len < 0)
 -                      return error(_("%s: negative length %s"), k, v);
 -              if (is_bool && shortlog_len)
 -                      shortlog_len = DEFAULT_MERGE_LOG_LEN;
 -              return 0;
 -      } else if (!strcmp(k, "merge.ff")) {
 +      else if (!strcmp(k, "merge.ff")) {
                int boolval = git_config_maybe_bool(k, v);
                if (0 <= boolval) {
                        allow_fast_forward = boolval;
                default_to_upstream = git_config_bool(k, v);
                return 0;
        }
 +      status = fmt_merge_msg_config(k, v, cb);
 +      if (status)
 +              return status;
        return git_diff_ui_config(k, v, cb);
  }
  
@@@ -711,7 -714,7 +711,7 @@@ static int try_merge_strategy(const cha
                                die(_("Unknown option for merge-recursive: -X%s"), xopts[x]);
  
                o.branch1 = head_arg;
 -              o.branch2 = remoteheads->item->util;
 +              o.branch2 = merge_remote_util(remoteheads->item)->name;
  
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
@@@ -768,7 -771,7 +768,7 @@@ int checkout_fast_forward(const unsigne
        memset(&t, 0, sizeof(t));
        memset(&dir, 0, sizeof(dir));
        dir.flags |= DIR_SHOW_IGNORED;
 -      dir.exclude_per_dir = ".gitignore";
 +      setup_standard_excludes(&dir);
        opts.dir = &dir;
  
        opts.head_idx = 1;
@@@ -844,22 -847,20 +844,22 @@@ static void add_strategies(const char *
  
  static void write_merge_msg(struct strbuf *msg)
  {
 -      int fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
 +      const char *filename = git_path("MERGE_MSG");
 +      int fd = open(filename, O_WRONLY | O_CREAT, 0666);
        if (fd < 0)
                die_errno(_("Could not open '%s' for writing"),
 -                        git_path("MERGE_MSG"));
 +                        filename);
        if (write_in_full(fd, msg->buf, msg->len) != msg->len)
 -              die_errno(_("Could not write to '%s'"), git_path("MERGE_MSG"));
 +              die_errno(_("Could not write to '%s'"), filename);
        close(fd);
  }
  
  static void read_merge_msg(struct strbuf *msg)
  {
 +      const char *filename = git_path("MERGE_MSG");
        strbuf_reset(msg);
 -      if (strbuf_read_file(msg, git_path("MERGE_MSG"), 0) < 0)
 -              die_errno(_("Could not read from '%s'"), git_path("MERGE_MSG"));
 +      if (strbuf_read_file(msg, filename, 0) < 0)
 +              die_errno(_("Could not read from '%s'"), filename);
  }
  
  static void write_merge_state(void);
@@@ -947,14 -948,13 +947,14 @@@ static int finish_automerge(struct comm
  
  static int suggest_conflicts(int renormalizing)
  {
 +      const char *filename;
        FILE *fp;
        int pos;
  
 -      fp = fopen(git_path("MERGE_MSG"), "a");
 +      filename = git_path("MERGE_MSG");
 +      fp = fopen(filename, "a");
        if (!fp)
 -              die_errno(_("Could not open '%s' for writing"),
 -                        git_path("MERGE_MSG"));
 +              die_errno(_("Could not open '%s' for writing"), filename);
        fprintf(fp, "\nConflicts:\n");
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
@@@ -1046,40 -1046,31 +1046,40 @@@ static int setup_with_upstream(const ch
  
  static void write_merge_state(void)
  {
 +      const char *filename;
        int fd;
        struct commit_list *j;
        struct strbuf buf = STRBUF_INIT;
  
 -      for (j = remoteheads; j; j = j->next)
 -              strbuf_addf(&buf, "%s\n",
 -                      sha1_to_hex(j->item->object.sha1));
 -      fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
 +      for (j = remoteheads; j; j = j->next) {
 +              unsigned const char *sha1;
 +              struct commit *c = j->item;
 +              if (c->util && merge_remote_util(c)->obj) {
 +                      sha1 = merge_remote_util(c)->obj->sha1;
 +              } else {
 +                      sha1 = c->object.sha1;
 +              }
 +              strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
 +      }
 +      filename = git_path("MERGE_HEAD");
 +      fd = open(filename, O_WRONLY | O_CREAT, 0666);
        if (fd < 0)
 -              die_errno(_("Could not open '%s' for writing"),
 -                        git_path("MERGE_HEAD"));
 +              die_errno(_("Could not open '%s' for writing"), filename);
        if (write_in_full(fd, buf.buf, buf.len) != buf.len)
 -              die_errno(_("Could not write to '%s'"), git_path("MERGE_HEAD"));
 +              die_errno(_("Could not write to '%s'"), filename);
        close(fd);
        strbuf_addch(&merge_msg, '\n');
        write_merge_msg(&merge_msg);
 -      fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
 +
 +      filename = git_path("MERGE_MODE");
 +      fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
        if (fd < 0)
 -              die_errno(_("Could not open '%s' for writing"),
 -                        git_path("MERGE_MODE"));
 +              die_errno(_("Could not open '%s' for writing"), filename);
        strbuf_reset(&buf);
        if (!allow_fast_forward)
                strbuf_addf(&buf, "no-ff");
        if (write_in_full(fd, buf.buf, buf.len) != buf.len)
 -              die_errno(_("Could not write to '%s'"), git_path("MERGE_MODE"));
 +              die_errno(_("Could not write to '%s'"), filename);
        close(fd);
  }
  
@@@ -1091,7 -1082,7 +1091,7 @@@ int cmd_merge(int argc, const char **ar
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
        const char *head_arg;
-       int flag, i;
+       int flag, i, ret = 0;
        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;
         * current branch.
         */
        branch = resolve_ref("HEAD", head_sha1, 0, &flag);
-       if (branch && !prefixcmp(branch, "refs/heads/"))
-               branch += 11;
+       if (branch) {
+               if (!prefixcmp(branch, "refs/heads/"))
+                       branch += 11;
+               branch = xstrdup(branch);
+       }
        if (!branch || is_null_sha1(head_sha1))
                head_commit = NULL;
        else
                parse_branch_merge_options(branch_mergeoptions);
        argc = parse_options(argc, argv, prefix, builtin_merge_options,
                        builtin_merge_usage, 0);
 +      if (shortlog_len < 0)
 +              shortlog_len = (merge_log_config > 0) ? merge_log_config : 0;
  
        if (verbosity < 0 && show_progress == -1)
                show_progress = 0;
                        die(_("There is no merge to abort (MERGE_HEAD missing)."));
  
                /* Invoke 'git reset --merge' */
-               return cmd_reset(nargc, nargv, prefix);
+               ret = cmd_reset(nargc, nargv, prefix);
+               goto done;
        }
  
        if (read_cache_unmerged())
                die(_("You cannot combine --no-ff with --ff-only."));
  
        if (!abort_current_merge) {
 -              if (!argc && default_to_upstream)
 -                      argc = setup_with_upstream(&argv);
 -              else if (argc == 1 && !strcmp(argv[0], "-"))
 +              if (!argc) {
 +                      if (default_to_upstream)
 +                              argc = setup_with_upstream(&argv);
 +                      else
 +                              die(_("No commit specified and merge.defaultToUpstream not set."));
 +              } else if (argc == 1 && !strcmp(argv[0], "-"))
                        argv[0] = "@{-1}";
        }
        if (!argc)
                argv += 2;
                argc -= 2;
        } else if (!head_commit) {
 -              struct object *remote_head;
 +              struct commit *remote_head;
                /*
                 * If the merged head is a valid one there is no reason
                 * to forbid "git merge" into a branch yet to be born.
                if (!allow_fast_forward)
                        die(_("Non-fast-forward commit does not make sense into "
                            "an empty head"));
 -              remote_head = want_commit(argv[0]);
 +              remote_head = get_merge_parent(argv[0]);
                if (!remote_head)
                        die(_("%s - not something we can merge"), argv[0]);
 -              read_empty(remote_head->sha1, 0);
 -              update_ref("initial pull", "HEAD", remote_head->sha1, NULL, 0,
 -                              DIE_ON_ERR);
 +              read_empty(remote_head->object.sha1, 0);
 +              update_ref("initial pull", "HEAD", remote_head->object.sha1,
 +                         NULL, 0, DIE_ON_ERR);
-               return 0;
+               goto done;
        } else {
                struct strbuf merge_names = STRBUF_INIT;
  
                head_arg = "HEAD";
  
                /*
 -               * All the rest are the commits being merged;
 -               * prepare the standard merge summary message to
 -               * be appended to the given message.  If remote
 -               * is invalid we will die later in the common
 -               * codepath so we discard the error in this
 -               * loop.
 +               * All the rest are the commits being merged; prepare
 +               * the standard merge summary message to be appended
 +               * to the given message.
                 */
                for (i = 0; i < argc; i++)
                        merge_name(argv[i], &merge_names);
  
                if (!have_message || shortlog_len) {
 -                      fmt_merge_msg(&merge_names, &merge_msg, !have_message,
 -                                    shortlog_len);
 +                      struct fmt_merge_msg_opts opts;
 +                      memset(&opts, 0, sizeof(opts));
 +                      opts.add_title = !have_message;
 +                      opts.shortlog_len = shortlog_len;
 +
 +                      fmt_merge_msg(&merge_names, &merge_msg, &opts);
                        if (merge_msg.len)
                                strbuf_setlen(&merge_msg, merge_msg.len - 1);
                }
        strbuf_reset(&buf);
  
        for (i = 0; i < argc; i++) {
 -              struct object *o;
 -              struct commit *commit;
 -
 -              o = want_commit(argv[i]);
 -              if (!o)
 +              struct commit *commit = get_merge_parent(argv[i]);
 +              if (!commit)
                        die(_("%s - not something we can merge"), argv[i]);
 -              commit = lookup_commit(o->sha1);
 -              commit->util = (void *)argv[i];
                remotes = &commit_list_insert(commit, remotes)->next;
 -
 -              strbuf_addf(&buf, "GITHEAD_%s", sha1_to_hex(o->sha1));
 +              strbuf_addf(&buf, "GITHEAD_%s",
 +                          sha1_to_hex(commit->object.sha1));
                setenv(buf.buf, argv[i], 1);
                strbuf_reset(&buf);
 +              if (merge_remote_util(commit) &&
 +                  merge_remote_util(commit)->obj &&
 +                  merge_remote_util(commit)->obj->type == OBJ_TAG) {
 +                      option_edit = 1;
 +                      allow_fast_forward = 0;
 +              }
        }
  
        if (!use_strategies) {
                 * but first the most common case of merging one remote.
                 */
                finish_up_to_date("Already up-to-date.");
-               return 0;
+               goto done;
        } else if (allow_fast_forward && !remoteheads->next &&
                        !common->next &&
                        !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;
 +              struct commit *commit;
                char hex[41];
  
                strcpy(hex, find_unique_abbrev(head_commit->object.sha1, DEFAULT_ABBREV));
                if (have_message)
                        strbuf_addstr(&msg,
                                " (no commit created; -m option ignored)");
 -              o = want_commit(sha1_to_hex(remoteheads->item->object.sha1));
 -              if (!o) {
 +              commit = remoteheads->item;
-               if (!commit)
-                       return 1;
++              if (!commit) {
+                       ret = 1;
+                       goto done;
+               }
  
                if (checkout_fast_forward(head_commit->object.sha1,
-                                         commit->object.sha1))
-                       return 1;
 -                                        remoteheads->item->object.sha1)) {
++                                        commit->object.sha1)) {
+                       ret = 1;
+                       goto done;
+               }
  
 -              finish(head_commit, o->sha1, msg.buf);
 +              finish(head_commit, commit->object.sha1, msg.buf);
                drop_save();
-               return 0;
+               goto done;
        } 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_commit->object.sha1, remoteheads->item->object.sha1))
-                               return merge_trivial(head_commit);
+                                              head_commit->object.sha1,
+                                              remoteheads->item->object.sha1)) {
+                               ret = merge_trivial(head_commit);
+                               goto done;
+                       }
                        printf(_("Nope.\n"));
                }
        } else {
                }
                if (up_to_date) {
                        finish_up_to_date("Already up-to-date. Yeeah!");
-                       return 0;
+                       goto done;
                }
        }
  
         * If we have a resulting tree, that means the strategy module
         * auto resolved the merge cleanly.
         */
-       if (automerge_was_ok)
-               return finish_automerge(head_commit, common, result_tree,
-                                       wt_strategy);
+       if (automerge_was_ok) {
+               ret = finish_automerge(head_commit, common, result_tree,
+                                      wt_strategy);
+               goto done;
+       }
  
        /*
         * Pick the result from the best strategy and have the user fix
                else
                        fprintf(stderr, _("Merge with strategy %s failed.\n"),
                                use_strategies[0]->name);
-               return 2;
+               ret = 2;
+               goto done;
        } else if (best_strategy == wt_strategy)
                ; /* We already have its result in the working tree. */
        else {
        else
                write_merge_state();
  
-       if (merge_was_ok) {
+       if (merge_was_ok)
                fprintf(stderr, _("Automatic merge went well; "
                        "stopped before committing as requested\n"));
-               return 0;
-       } else
-               return suggest_conflicts(option_renormalize);
+       else
+               ret = suggest_conflicts(option_renormalize);
+ done:
+       free((char *)branch);
+       return ret;
  }
diff --combined builtin/tag.c
index cca120552a89bf6be4a5fe2d9b187eba2af04d15,439249d466ccb9d3e4d78764b8b4b8c44536a7b5..efb98726955837294190fcef365e45b0dd5f5396
@@@ -14,7 -14,6 +14,7 @@@
  #include "parse-options.h"
  #include "diff.h"
  #include "revision.h"
 +#include "gpg-interface.h"
  
  static const char * const git_tag_usage[] = {
        "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
@@@ -24,6 -23,8 +24,6 @@@
        NULL
  };
  
 -static char signingkey[1000];
 -
  struct tag_filter {
        const char **patterns;
        int lines;
@@@ -173,7 -174,7 +173,7 @@@ static int for_each_tag_name(const cha
                        had_error = 1;
                        continue;
                }
-               if (!resolve_ref(ref, sha1, 1, NULL)) {
+               if (read_ref(ref, sha1)) {
                        error(_("tag '%s' not found."), *p);
                        had_error = 1;
                        continue;
@@@ -207,7 -208,60 +207,7 @@@ static int verify_tag(const char *name
  
  static int do_sign(struct strbuf *buffer)
  {
 -      struct child_process gpg;
 -      const char *args[4];
 -      char *bracket;
 -      int len;
 -      int i, j;
 -
 -      if (!*signingkey) {
 -              if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
 -                              sizeof(signingkey)) > sizeof(signingkey) - 1)
 -                      return error(_("committer info too long."));
 -              bracket = strchr(signingkey, '>');
 -              if (bracket)
 -                      bracket[1] = '\0';
 -      }
 -
 -      /* When the username signingkey is bad, program could be terminated
 -       * because gpg exits without reading and then write gets SIGPIPE. */
 -      signal(SIGPIPE, SIG_IGN);
 -
 -      memset(&gpg, 0, sizeof(gpg));
 -      gpg.argv = args;
 -      gpg.in = -1;
 -      gpg.out = -1;
 -      args[0] = "gpg";
 -      args[1] = "-bsau";
 -      args[2] = signingkey;
 -      args[3] = NULL;
 -
 -      if (start_command(&gpg))
 -              return error(_("could not run gpg."));
 -
 -      if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
 -              close(gpg.in);
 -              close(gpg.out);
 -              finish_command(&gpg);
 -              return error(_("gpg did not accept the tag data"));
 -      }
 -      close(gpg.in);
 -      len = strbuf_read(buffer, gpg.out, 1024);
 -      close(gpg.out);
 -
 -      if (finish_command(&gpg) || !len || len < 0)
 -              return error(_("gpg failed to sign the tag"));
 -
 -      /* Strip CR from the line endings, in case we are on Windows. */
 -      for (i = j = 0; i < buffer->len; i++)
 -              if (buffer->buf[i] != '\r') {
 -                      if (i != j)
 -                              buffer->buf[j] = buffer->buf[i];
 -                      j++;
 -              }
 -      strbuf_setlen(buffer, j);
 -
 -      return 0;
 +      return sign_buffer(buffer, buffer, get_signing_key());
  }
  
  static const char tag_template[] =
        "# Write a tag message\n"
        "#\n");
  
 -static void set_signingkey(const char *value)
 -{
 -      if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
 -              die(_("signing key value too long (%.10s...)"), value);
 -}
 -
  static int git_tag_config(const char *var, const char *value, void *cb)
  {
 -      if (!strcmp(var, "user.signingkey")) {
 -              if (!value)
 -                      return config_error_nonbool(var);
 -              set_signingkey(value);
 -              return 0;
 -      }
 -
 +      int status = git_gpg_config(var, value, cb);
 +      if (status)
 +              return status;
        return git_default_config(var, value, cb);
  }
  
@@@ -399,7 -463,7 +399,7 @@@ int cmd_tag(int argc, const char **argv
  
        if (keyid) {
                sign = 1;
 -              set_signingkey(keyid);
 +              set_signing_key(keyid);
        }
        if (sign)
                annotate = 1;
        if (strbuf_check_tag_ref(&ref, tag))
                die(_("'%s' is not a valid tag name."), tag);
  
-       if (!resolve_ref(ref.buf, prev, 1, NULL))
+       if (read_ref(ref.buf, prev))
                hashclr(prev);
        else if (!force)
                die(_("tag '%s' already exists"), tag);
diff --combined cache.h
index d29ce99631653997ae0f9236d286b9ab28a1e721,5badece71b1e473b6a2c0f0e446e5ce030b2ecd5..8c98d056674c9a426215bf93398fe922a1a90679
+++ b/cache.h
@@@ -306,7 -306,7 +306,7 @@@ static inline unsigned int canon_mode(u
  }
  
  #define flexible_size(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
 -#define cache_entry_size(len) flexible_size(cache_entry,len)
 +#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
  #define ondisk_cache_entry_size(len) flexible_size(ondisk_cache_entry,len)
  #define ondisk_cache_entry_extended_size(len) flexible_size(ondisk_cache_entry_extended,len)
  
@@@ -316,6 -316,7 +316,6 @@@ struct index_state 
        struct string_list *resolve_undo;
        struct cache_tree *cache_tree;
        struct cache_time timestamp;
 -      void *alloc;
        unsigned name_hash_initialized : 1,
                 initialized : 1;
        struct hash_table name_hash;
@@@ -831,6 -832,8 +831,8 @@@ static inline int get_sha1_with_context
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  
  extern char *sha1_to_hex(const unsigned char *sha1);  /* static buffer result! */
+ extern int read_ref_full(const char *filename, unsigned char *sha1,
+                        int reading, int *flags);
  extern int read_ref(const char *filename, unsigned char *sha1);
  
  /*
@@@ -872,7 -875,7 +874,7 @@@ extern int get_sha1_mb(const char *str
  
  extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
  extern const char *ref_rev_parse_rules[];
 -extern const char *ref_fetch_rules[];
 +#define ref_fetch_rules ref_rev_parse_rules
  
  extern int create_symref(const char *ref, const char *refs_heads_master, const char *logmsg);
  extern int validate_headref(const char *ref);
diff --combined notes-merge.c
index ce10aacf2978024eda0234482964577e9235f2d2,e33c2c9f165ea3ae883860066aca8f91018d2a75..2de27af751476a62dc9b0afe48eb360f83a0702b
@@@ -21,6 -21,14 +21,6 @@@ void init_notes_merge_options(struct no
        o->verbosity = NOTES_MERGE_VERBOSITY_DEFAULT;
  }
  
 -#define OUTPUT(o, v, ...) \
 -      do { \
 -              if ((o)->verbosity >= (v)) { \
 -                      printf(__VA_ARGS__); \
 -                      puts(""); \
 -              } \
 -      } while (0)
 -
  static int path_to_sha1(const char *path, unsigned char *sha1)
  {
        char hex_sha1[40];
@@@ -384,26 -392,21 +384,26 @@@ static int merge_one_change_manual(stru
  
        strbuf_addf(&(o->commit_msg), "\t%s\n", sha1_to_hex(p->obj));
  
 -      OUTPUT(o, 2, "Auto-merging notes for %s", sha1_to_hex(p->obj));
 +      if (o->verbosity >= 2)
 +              printf("Auto-merging notes for %s\n", sha1_to_hex(p->obj));
        check_notes_merge_worktree(o);
        if (is_null_sha1(p->local)) {
                /* D/F conflict, checkout p->remote */
                assert(!is_null_sha1(p->remote));
 -              OUTPUT(o, 1, "CONFLICT (delete/modify): Notes for object %s "
 -                     "deleted in %s and modified in %s. Version from %s "
 -                     "left in tree.", sha1_to_hex(p->obj), lref, rref, rref);
 +              if (o->verbosity >= 1)
 +                      printf("CONFLICT (delete/modify): Notes for object %s "
 +                              "deleted in %s and modified in %s. Version from %s "
 +                              "left in tree.\n",
 +                              sha1_to_hex(p->obj), lref, rref, rref);
                write_note_to_worktree(p->obj, p->remote);
        } else if (is_null_sha1(p->remote)) {
                /* D/F conflict, checkout p->local */
                assert(!is_null_sha1(p->local));
 -              OUTPUT(o, 1, "CONFLICT (delete/modify): Notes for object %s "
 -                     "deleted in %s and modified in %s. Version from %s "
 -                     "left in tree.", sha1_to_hex(p->obj), rref, lref, lref);
 +              if (o->verbosity >= 1)
 +                      printf("CONFLICT (delete/modify): Notes for object %s "
 +                              "deleted in %s and modified in %s. Version from %s "
 +                              "left in tree.\n",
 +                              sha1_to_hex(p->obj), rref, lref, lref);
                write_note_to_worktree(p->obj, p->local);
        } else {
                /* "regular" conflict, checkout result of ll_merge() */
                        reason = "add/add";
                assert(!is_null_sha1(p->local));
                assert(!is_null_sha1(p->remote));
 -              OUTPUT(o, 1, "CONFLICT (%s): Merge conflict in notes for "
 -                     "object %s", reason, sha1_to_hex(p->obj));
 +              if (o->verbosity >= 1)
 +                      printf("CONFLICT (%s): Merge conflict in notes for "
 +                              "object %s\n", reason, sha1_to_hex(p->obj));
                ll_merge_in_worktree(o, p);
        }
  
@@@ -436,30 -438,24 +436,30 @@@ static int merge_one_change(struct note
        case NOTES_MERGE_RESOLVE_MANUAL:
                return merge_one_change_manual(o, p, t);
        case NOTES_MERGE_RESOLVE_OURS:
 -              OUTPUT(o, 2, "Using local notes for %s", sha1_to_hex(p->obj));
 +              if (o->verbosity >= 2)
 +                      printf("Using local notes for %s\n",
 +                                              sha1_to_hex(p->obj));
                /* nothing to do */
                return 0;
        case NOTES_MERGE_RESOLVE_THEIRS:
 -              OUTPUT(o, 2, "Using remote notes for %s", sha1_to_hex(p->obj));
 +              if (o->verbosity >= 2)
 +                      printf("Using remote notes for %s\n",
 +                                              sha1_to_hex(p->obj));
                if (add_note(t, p->obj, p->remote, combine_notes_overwrite))
                        die("BUG: combine_notes_overwrite failed");
                return 0;
        case NOTES_MERGE_RESOLVE_UNION:
 -              OUTPUT(o, 2, "Concatenating local and remote notes for %s",
 -                     sha1_to_hex(p->obj));
 +              if (o->verbosity >= 2)
 +                      printf("Concatenating local and remote notes for %s\n",
 +                                                      sha1_to_hex(p->obj));
                if (add_note(t, p->obj, p->remote, combine_notes_concatenate))
                        die("failed to concatenate notes "
                            "(combine_notes_concatenate)");
                return 0;
        case NOTES_MERGE_RESOLVE_CAT_SORT_UNIQ:
 -              OUTPUT(o, 2, "Concatenating unique lines in local and remote "
 -                     "notes for %s", sha1_to_hex(p->obj));
 +              if (o->verbosity >= 2)
 +                      printf("Concatenating unique lines in local and remote "
 +                              "notes for %s\n", sha1_to_hex(p->obj));
                if (add_note(t, p->obj, p->remote, combine_notes_cat_sort_uniq))
                        die("failed to concatenate notes "
                            "(combine_notes_cat_sort_uniq)");
@@@ -522,9 -518,8 +522,9 @@@ static int merge_from_diffs(struct note
        conflicts = merge_changes(o, changes, &num_changes, t);
        free(changes);
  
 -      OUTPUT(o, 4, "Merge result: %i unmerged notes and a %s notes tree",
 -             conflicts, t->dirty ? "dirty" : "clean");
 +      if (o->verbosity >= 4)
 +              printf("Merge result: %i unmerged notes and a %s notes tree\n",
 +                      conflicts, t->dirty ? "dirty" : "clean");
  
        return conflicts ? -1 : 1;
  }
@@@ -573,7 -568,7 +573,7 @@@ int notes_merge(struct notes_merge_opti
               o->local_ref, o->remote_ref);
  
        /* Dereference o->local_ref into local_sha1 */
-       if (!resolve_ref(o->local_ref, local_sha1, 0, NULL))
+       if (read_ref_full(o->local_ref, local_sha1, 0, NULL))
                die("Failed to resolve local notes ref '%s'", o->local_ref);
        else if (!check_refname_format(o->local_ref, 0) &&
                is_null_sha1(local_sha1))
        if (!bases) {
                base_sha1 = null_sha1;
                base_tree_sha1 = EMPTY_TREE_SHA1_BIN;
 -              OUTPUT(o, 4, "No merge base found; doing history-less merge");
 +              if (o->verbosity >= 4)
 +                      printf("No merge base found; doing history-less merge\n");
        } else if (!bases->next) {
                base_sha1 = bases->item->object.sha1;
                base_tree_sha1 = bases->item->tree->object.sha1;
 -              OUTPUT(o, 4, "One merge base found (%.7s)",
 -                     sha1_to_hex(base_sha1));
 +              if (o->verbosity >= 4)
 +                      printf("One merge base found (%.7s)\n",
 +                              sha1_to_hex(base_sha1));
        } else {
                /* TODO: How to handle multiple merge-bases? */
                base_sha1 = bases->item->object.sha1;
                base_tree_sha1 = bases->item->tree->object.sha1;
 -              OUTPUT(o, 3, "Multiple merge bases found. Using the first "
 -                     "(%.7s)", sha1_to_hex(base_sha1));
 +              if (o->verbosity >= 3)
 +                      printf("Multiple merge bases found. Using the first "
 +                              "(%.7s)\n", sha1_to_hex(base_sha1));
        }
  
 -      OUTPUT(o, 4, "Merging remote commit %.7s into local commit %.7s with "
 -             "merge-base %.7s", sha1_to_hex(remote->object.sha1),
 -             sha1_to_hex(local->object.sha1), sha1_to_hex(base_sha1));
 +      if (o->verbosity >= 4)
 +              printf("Merging remote commit %.7s into local commit %.7s with "
 +                      "merge-base %.7s\n", sha1_to_hex(remote->object.sha1),
 +                      sha1_to_hex(local->object.sha1),
 +                      sha1_to_hex(base_sha1));
  
        if (!hashcmp(remote->object.sha1, base_sha1)) {
                /* Already merged; result == local commit */
 -              OUTPUT(o, 2, "Already up-to-date!");
 +              if (o->verbosity >= 2)
 +                      printf("Already up-to-date!\n");
                hashcpy(result_sha1, local->object.sha1);
                goto found_result;
        }
        if (!hashcmp(local->object.sha1, base_sha1)) {
                /* Fast-forward; result == remote commit */
 -              OUTPUT(o, 2, "Fast-forward");
 +              if (o->verbosity >= 2)
 +                      printf("Fast-forward\n");
                hashcpy(result_sha1, remote->object.sha1);
                goto found_result;
        }
@@@ -697,9 -685,8 +697,9 @@@ int notes_merge_commit(struct notes_mer
        int path_len = strlen(path), i;
        const char *msg = strstr(partial_commit->buffer, "\n\n");
  
 -      OUTPUT(o, 3, "Committing notes in notes merge worktree at %.*s",
 -             path_len - 1, path);
 +      if (o->verbosity >= 3)
 +              printf("Committing notes in notes merge worktree at %.*s\n",
 +                      path_len - 1, path);
  
        if (!msg || msg[2] == '\0')
                die("partial notes commit has empty message");
                unsigned char obj_sha1[20], blob_sha1[20];
  
                if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) {
 -                      OUTPUT(o, 3, "Skipping non-SHA1 entry '%s'", ent->name);
 +                      if (o->verbosity >= 3)
 +                              printf("Skipping non-SHA1 entry '%s'\n",
 +                                                              ent->name);
                        continue;
                }
  
                if (add_note(partial_tree, obj_sha1, blob_sha1, NULL))
                        die("Failed to add resolved note '%s' to notes tree",
                            ent->name);
 -              OUTPUT(o, 4, "Added resolved note for object %s: %s",
 -                     sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
 +              if (o->verbosity >= 4)
 +                      printf("Added resolved note for object %s: %s\n",
 +                              sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1));
        }
  
        create_notes_commit(partial_tree, partial_commit->parents, msg,
                            result_sha1);
 -      OUTPUT(o, 4, "Finalized notes merge commit: %s",
 -             sha1_to_hex(result_sha1));
 +      if (o->verbosity >= 4)
 +              printf("Finalized notes merge commit: %s\n",
 +                      sha1_to_hex(result_sha1));
        free(path);
        return 0;
  }
@@@ -749,8 -732,7 +749,8 @@@ int notes_merge_abort(struct notes_merg
        int ret;
  
        strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
 -      OUTPUT(o, 3, "Removing notes merge worktree at %s", buf.buf);
 +      if (o->verbosity >= 3)
 +              printf("Removing notes merge worktree at %s\n", buf.buf);
        ret = remove_dir_recursively(&buf, 0);
        strbuf_release(&buf);
        return ret;
diff --combined refs.c
index c74b6e2a70222386383edb89789dbb7880768157,44c1c86e187568f8cf1224f2f540cb6b73db3469..f5cb297292f5ae577e3d2719f5f78512aae8cf28
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -48,7 -48,7 +48,7 @@@ static const char *parse_ref_line(char 
  }
  
  static void add_ref(const char *name, const unsigned char *sha1,
 -                  int flag, struct ref_array *refs,
 +                  int flag, int check_name, struct ref_array *refs,
                    struct ref_entry **new_entry)
  {
        int len;
@@@ -59,8 -59,7 +59,8 @@@
        entry = xmalloc(sizeof(struct ref_entry) + len);
        hashcpy(entry->sha1, sha1);
        hashclr(entry->peeled);
 -      if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
 +      if (check_name &&
 +          check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
                die("Reference has invalid format: '%s'", name);
        memcpy(entry->name, name, len);
        entry->flag = flag;
@@@ -235,7 -234,7 +235,7 @@@ static void read_packed_refs(FILE *f, s
  
                name = parse_ref_line(refline, sha1);
                if (name) {
 -                      add_ref(name, sha1, flag, array, &last);
 +                      add_ref(name, sha1, flag, 1, array, &last);
                        continue;
                }
                if (last &&
  
  void add_extra_ref(const char *name, const unsigned char *sha1, int flag)
  {
 -      add_ref(name, sha1, flag, &extra_refs, NULL);
 +      add_ref(name, sha1, flag, 0, &extra_refs, NULL);
  }
  
  void clear_extra_refs(void)
@@@ -334,11 -333,12 +334,11 @@@ static void get_ref_dir(const char *sub
                                        hashclr(sha1);
                                        flag |= REF_ISBROKEN;
                                }
-                       } else if (!resolve_ref(ref, sha1, 1, &flag)) {
 -                      } else
 -                              if (read_ref_full(ref, sha1, 1, &flag)) {
 -                                      hashclr(sha1);
 -                                      flag |= REF_ISBROKEN;
 -                              }
 -                      add_ref(ref, sha1, flag, array, NULL);
++                      } else if (read_ref_full(ref, sha1, 1, &flag)) {
 +                              hashclr(sha1);
 +                              flag |= REF_ISBROKEN;
 +                      }
 +                      add_ref(ref, sha1, flag, 1, array, NULL);
                }
                free(ref);
                closedir(dir);
@@@ -612,13 -612,18 +612,18 @@@ struct ref_filter 
        void *cb_data;
  };
  
- int read_ref(const char *ref, unsigned char *sha1)
+ int read_ref_full(const char *ref, unsigned char *sha1, int reading, int *flags)
  {
-       if (resolve_ref(ref, sha1, 1, NULL))
+       if (resolve_ref(ref, sha1, reading, flags))
                return 0;
        return -1;
  }
  
+ int read_ref(const char *ref, unsigned char *sha1)
+ {
+       return read_ref_full(ref, sha1, 1, NULL);
+ }
  #define DO_FOR_EACH_INCLUDE_BROKEN 01
  static int do_one_ref(const char *base, each_ref_fn fn, int trim,
                      int flags, void *cb_data, struct ref_entry *entry)
@@@ -663,7 -668,7 +668,7 @@@ int peel_ref(const char *ref, unsigned 
                goto fallback;
        }
  
-       if (!resolve_ref(ref, base, 1, &flag))
+       if (read_ref_full(ref, base, 1, &flag))
                return -1;
  
        if ((flag & REF_ISPACKED)) {
@@@ -746,7 -751,7 +751,7 @@@ static int do_head_ref(const char *subm
                return 0;
        }
  
-       if (resolve_ref("HEAD", sha1, 1, &flag))
+       if (!read_ref_full("HEAD", sha1, 1, &flag))
                return fn("HEAD", sha1, flag, cb_data);
  
        return 0;
@@@ -826,7 -831,7 +831,7 @@@ int head_ref_namespaced(each_ref_fn fn
        int flag;
  
        strbuf_addf(&buf, "%sHEAD", get_git_namespace());
-       if (resolve_ref(buf.buf, sha1, 1, &flag))
+       if (!read_ref_full(buf.buf, sha1, 1, &flag))
                ret = fn(buf.buf, sha1, flag, cb_data);
        strbuf_release(&buf);
  
@@@ -998,6 -1003,13 +1003,6 @@@ const char *ref_rev_parse_rules[] = 
        NULL
  };
  
 -const char *ref_fetch_rules[] = {
 -      "%.*s",
 -      "refs/%.*s",
 -      "refs/heads/%.*s",
 -      NULL
 -};
 -
  int refname_match(const char *abbrev_name, const char *full_name, const char **rules)
  {
        const char **p;
  static struct ref_lock *verify_lock(struct ref_lock *lock,
        const unsigned char *old_sha1, int mustexist)
  {
-       if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
+       if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
                error("Can't verify ref %s", lock->ref_name);
                unlock_ref(lock);
                return NULL;
@@@ -1370,7 -1382,8 +1375,8 @@@ int rename_ref(const char *oldref, cons
                goto rollback;
        }
  
-       if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
+       if (!read_ref_full(newref, sha1, 1, &flag) &&
+           delete_ref(newref, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
                        if (remove_empty_directories(git_path("%s", newref))) {
                                error("Directory not empty: %s", newref);
@@@ -1922,7 -1935,7 +1928,7 @@@ static int do_for_each_reflog(const cha
                                retval = do_for_each_reflog(log, fn, cb_data);
                        } else {
                                unsigned char sha1[20];
-                               if (!resolve_ref(log, sha1, 0, NULL))
+                               if (read_ref_full(log, sha1, 0, NULL))
                                        retval = error("bad ref for %s", log);
                                else
                                        retval = fn(log, sha1, 0, cb_data);
@@@ -2065,7 -2078,6 +2071,6 @@@ char *shorten_unambiguous_ref(const cha
                 */
                for (j = 0; j < rules_to_fail; j++) {
                        const char *rule = ref_rev_parse_rules[j];
-                       unsigned char short_objectname[20];
                        char refname[PATH_MAX];
  
                        /* skip matched rule */
                         */
                        mksnpath(refname, sizeof(refname),
                                 rule, short_name_len, short_name);
-                       if (!read_ref(refname, short_objectname))
+                       if (ref_exists(refname))
                                break;
                }