Merge branch 'jk/xstrfmt'
authorJunio C Hamano <gitster@pobox.com>
Wed, 9 Jul 2014 18:34:05 +0000 (11:34 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Jul 2014 18:34:05 +0000 (11:34 -0700)
* jk/xstrfmt:
setup_git_env(): introduce git_path_from_env() helper
unique_path: fix unlikely heap overflow
walker_fetch: fix minor memory leak
merge: use argv_array when spawning merge strategy
sequencer: use argv_array_pushf
setup_git_env: use git_pathdup instead of xmalloc + sprintf
use xstrfmt to replace xmalloc + strcpy/strcat
use xstrfmt to replace xmalloc + sprintf
use xstrdup instead of xmalloc + strcpy
use xstrfmt in favor of manual size calculations
strbuf: add xstrfmt helper

1  2 
builtin/apply.c
builtin/fetch.c
builtin/fmt-merge-msg.c
http-push.c
merge-recursive.c
remote.c
sequencer.c
sha1_name.c
unpack-trees.c
diff --combined builtin/apply.c
index bc924ab2d03a17477fbbe9ea6a25a704963f85b5,b79691053ff26c853da7884b6d0484285801c6e7..16cc93587fd78f620bdcc0d0841ca98ac3394d9e
@@@ -1281,9 -1281,7 +1281,7 @@@ static int parse_git_header(const char 
         */
        patch->def_name = git_header_name(line, len);
        if (patch->def_name && root) {
-               char *s = xmalloc(root_len + strlen(patch->def_name) + 1);
-               strcpy(s, root);
-               strcpy(s + root_len, patch->def_name);
+               char *s = xstrfmt("%s%s", root, patch->def_name);
                free(patch->def_name);
                patch->def_name = s;
        }
@@@ -3847,10 -3845,9 +3845,10 @@@ static void add_index_file(const char *
        ce->ce_flags = create_ce_flags(0);
        ce->ce_namelen = namelen;
        if (S_ISGITLINK(mode)) {
 -              const char *s = buf;
 +              const char *s;
  
 -              if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
 +              if (!skip_prefix(buf, "Subproject commit ", &s) ||
 +                  get_sha1_hex(s, ce->sha1))
                        die(_("corrupt patch for submodule %s"), path);
        } else {
                if (!cached) {
diff --combined builtin/fetch.c
index dd46b61d9a31475e3d5289e41e6d7f57a0264b6f,40d989f9ffa17b3f9c5b59ad225b5dedbea73ba6..e8d0cca3e4110e1ae6db82f21c899c44460cb849
@@@ -45,8 -45,6 +45,8 @@@ static struct transport *gsecondary
  static const char *submodule_prefix = "";
  static const char *recurse_submodules_default;
  static int shown_url = 0;
 +static int refmap_alloc, refmap_nr;
 +static const char **refmap_array;
  
  static int option_parse_recurse_submodules(const struct option *opt,
                                   const char *arg, int unset)
@@@ -71,19 -69,6 +71,19 @@@ static int git_fetch_config(const char 
        return 0;
  }
  
 +static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
 +{
 +      ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
 +
 +      /*
 +       * "git fetch --refmap='' origin foo"
 +       * can be used to tell the command not to store anywhere
 +       */
 +      if (*arg)
 +              refmap_array[refmap_nr++] = arg;
 +      return 0;
 +}
 +
  static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "all", &all,
                   N_("default mode for recursion"), PARSE_OPT_HIDDEN },
        OPT_BOOL(0, "update-shallow", &update_shallow,
                 N_("accept refs that update .git/shallow")),
 +      { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
 +        N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
        OPT_END()
  };
  
@@@ -295,9 -278,6 +295,9 @@@ static struct ref *get_ref_map(struct t
        const struct ref *remote_refs = transport_get_remote_refs(transport);
  
        if (refspec_count) {
 +              struct refspec *fetch_refspec;
 +              int fetch_refspec_nr;
 +
                for (i = 0; i < refspec_count; i++) {
                        get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
                        if (refspecs[i].dst && refspecs[i].dst[0])
                 * by ref_remove_duplicates() in favor of one of these
                 * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
 -              for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
 -                      get_fetch_map(ref_map, &transport->remote->fetch[i],
 -                                    &oref_tail, 1);
 +              if (refmap_array) {
 +                      fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);
 +                      fetch_refspec_nr = refmap_nr;
 +              } else {
 +                      fetch_refspec = transport->remote->fetch;
 +                      fetch_refspec_nr = transport->remote->fetch_refspec_nr;
 +              }
 +
 +              for (i = 0; i < fetch_refspec_nr; i++)
 +                      get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
  
                if (tags == TAGS_SET)
                        get_fetch_map(remote_refs, tag_refspec, &tail, 0);
 +      } else if (refmap_array) {
 +              die("--refmap option is only meaningful with command-line refspec(s).");
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
@@@ -1082,16 -1053,11 +1082,11 @@@ static int fetch_one(struct remote *rem
                refs = xcalloc(argc + 1, sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
-                               char *ref;
                                i++;
                                if (i >= argc)
                                        die(_("You need to specify a tag name."));
-                               ref = xmalloc(strlen(argv[i]) * 2 + 22);
-                               strcpy(ref, "refs/tags/");
-                               strcat(ref, argv[i]);
-                               strcat(ref, ":refs/tags/");
-                               strcat(ref, argv[i]);
-                               refs[j++] = ref;
+                               refs[j++] = xstrfmt("refs/tags/%s:refs/tags/%s",
+                                                   argv[i], argv[i]);
                        } else
                                refs[j++] = argv[i];
                }
diff --combined builtin/fmt-merge-msg.c
index 971e802c6f2945cb6218b7a98c9a30fdc0c9f94b,c462e19e231e0019e371bf92cd75d165fe5db3ab..79df05ef526bcd1a3be2971c9d467a930001639d
@@@ -100,8 -100,7 +100,8 @@@ static int handle_line(char *line, stru
  {
        int i, len = strlen(line);
        struct origin_data *origin_data;
 -      char *src, *origin;
 +      char *src;
 +      const char *origin;
        struct src_data *src_data;
        struct string_list_item *item;
        int pulling_head = 0;
                origin = line;
                string_list_append(&src_data->tag, origin + 4);
                src_data->head_status |= 2;
 -      } else if (starts_with(line, "remote-tracking branch ")) {
 -              origin = line + strlen("remote-tracking branch ");
 +      } else if (skip_prefix(line, "remote-tracking branch ", &origin)) {
                string_list_append(&src_data->r_branch, origin);
                src_data->head_status |= 2;
        } else {
                int len = strlen(origin);
                if (origin[0] == '\'' && origin[len - 1] == '\'')
                        origin = xmemdupz(origin + 1, len - 2);
-       } else {
-               char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
-               sprintf(new_origin, "%s of %s", origin, src);
-               origin = new_origin;
-       }
+       } else
+               origin = xstrfmt("%s of %s", origin, src);
        if (strcmp(".", src))
                origin_data->is_local_branch = 0;
        string_list_append(&origins, origin)->util = origin_data;
@@@ -230,14 -227,12 +227,14 @@@ static void add_branch_desc(struct strb
  static void record_person(int which, struct string_list *people,
                          struct commit *commit)
  {
 +      const char *buffer;
        char *name_buf, *name, *name_end;
        struct string_list_item *elem;
        const char *field;
  
        field = (which == 'a') ? "\nauthor " : "\ncommitter ";
 -      name = strstr(commit->buffer, field);
 +      buffer = get_commit_buffer(commit, NULL);
 +      name = strstr(buffer, field);
        if (!name)
                return;
        name += strlen(field);
        if (name_end < name)
                return;
        name_buf = xmemdupz(name, name_end - name + 1);
 +      unuse_commit_buffer(commit, buffer);
  
        elem = string_list_lookup(people, name_buf);
        if (!elem) {
@@@ -300,8 -294,8 +297,8 @@@ static void credit_people(struct strbu
        if (!them->nr ||
            (them->nr == 1 &&
             me &&
 -           (me = skip_prefix(me, them->items->string)) != NULL &&
 -           skip_prefix(me, " <")))
 +           skip_prefix(me, them->items->string, &me) &&
 +           starts_with(me, " <")))
                return;
        strbuf_addf(out, "\n%c %s ", comment_line_char, label);
        add_people_count(out, them);
diff --combined http-push.c
index c5c95e85ea305540b7863512617adca35b858534,390f74c97b187158e94f77d0dfefdba3712421e1..6c3cc1725a9461876c7db11b9818eb553afb2fc2
@@@ -719,10 -719,14 +719,10 @@@ static int fetch_indices(void
        return ret;
  }
  
 -static void one_remote_object(const char *hex)
 +static void one_remote_object(const unsigned char *sha1)
  {
 -      unsigned char sha1[20];
        struct object *obj;
  
 -      if (get_sha1_hex(hex, sha1) != 0)
 -              return;
 -
        obj = lookup_object(sha1);
        if (!obj)
                obj = parse_object(sha1);
@@@ -763,15 -767,13 +763,13 @@@ static void handle_new_lock_ctx(struct 
  
        if (tag_closed && ctx->cdata) {
                if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
-                       lock->owner = xmalloc(strlen(ctx->cdata) + 1);
-                       strcpy(lock->owner, ctx->cdata);
+                       lock->owner = xstrdup(ctx->cdata);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TIMEOUT)) {
 -                      if (starts_with(ctx->cdata, "Second-"))
 -                              lock->timeout =
 -                                      strtol(ctx->cdata + 7, NULL, 10);
 +                      const char *arg;
 +                      if (skip_prefix(ctx->cdata, "Second-", &arg))
 +                              lock->timeout = strtol(arg, NULL, 10);
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
-                       lock->token = xmalloc(strlen(ctx->cdata) + 1);
-                       strcpy(lock->token, ctx->cdata);
+                       lock->token = xstrdup(ctx->cdata);
  
                        git_SHA1_Init(&sha_ctx);
                        git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token));
@@@ -852,8 -854,7 +850,7 @@@ static struct remote_lock *lock_remote(
        struct xml_ctx ctx;
        char *escaped;
  
-       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", repo->url, path);
+       url = xstrfmt("%s%s", repo->url, path);
  
        /* Make sure leading directories exist for the remote ref */
        ep = strchr(url + strlen(repo->url) + 1, '/');
@@@ -1016,38 -1017,26 +1013,38 @@@ static void remote_ls(const char *path
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData);
  
 +/* extract hex from sharded "xx/x{40}" filename */
 +static int get_sha1_hex_from_objpath(const char *path, unsigned char *sha1)
 +{
 +      char hex[40];
 +
 +      if (strlen(path) != 41)
 +              return -1;
 +
 +      memcpy(hex, path, 2);
 +      path += 2;
 +      path++; /* skip '/' */
 +      memcpy(hex, path, 38);
 +
 +      return get_sha1_hex(hex, sha1);
 +}
 +
  static void process_ls_object(struct remote_ls_ctx *ls)
  {
        unsigned int *parent = (unsigned int *)ls->userData;
 -      char *path = ls->dentry_name;
 -      char *obj_hex;
 +      const char *path = ls->dentry_name;
 +      unsigned char sha1[20];
  
        if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) {
                remote_dir_exists[*parent] = 1;
                return;
        }
  
 -      if (strlen(path) != 49)
 +      if (!skip_prefix(path, "objects/", &path) ||
 +          get_sha1_hex_from_objpath(path, sha1))
                return;
 -      path += 8;
 -      obj_hex = xmalloc(strlen(path));
 -      /* NB: path is not null-terminated, can not use strlcpy here */
 -      memcpy(obj_hex, path, 2);
 -      strcpy(obj_hex + 2, path + 3);
 -      one_remote_object(obj_hex);
 -      free(obj_hex);
 +
 +      one_remote_object(sha1);
  }
  
  static void process_ls_ref(struct remote_ls_ctx *ls)
@@@ -1125,7 -1114,7 +1122,7 @@@ static void remote_ls(const char *path
                      void (*userFunc)(struct remote_ls_ctx *ls),
                      void *userData)
  {
-       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       char *url = xstrfmt("%s%s", repo->url, path);
        struct active_request_slot *slot;
        struct slot_results results;
        struct strbuf in_buffer = STRBUF_INIT;
        ls.userData = userData;
        ls.userFunc = userFunc;
  
-       sprintf(url, "%s%s", repo->url, path);
        strbuf_addf(&out_buffer.buf, PROPFIND_ALL_REQUEST);
  
        dav_headers = curl_slist_append(dav_headers, "Depth: 1");
@@@ -1544,10 -1531,9 +1539,9 @@@ static void update_remote_info_refs(str
  
  static int remote_exists(const char *path)
  {
-       char *url = xmalloc(strlen(repo->url) + strlen(path) + 1);
+       char *url = xstrfmt("%s%s", repo->url, path);
        int ret;
  
-       sprintf(url, "%s%s", repo->url, path);
  
        switch (http_get_strbuf(url, NULL, NULL)) {
        case HTTP_OK:
  
  static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
  {
-       char *url;
+       char *url = xstrfmt("%s%s", repo->url, path);
        struct strbuf buffer = STRBUF_INIT;
 +      const char *name;
  
-       url = xmalloc(strlen(repo->url) + strlen(path) + 1);
-       sprintf(url, "%s%s", repo->url, path);
        if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)
                die("Couldn't get %s for remote symref\n%s", url,
                    curl_errorstr);
                return;
  
        /* If it's a symref, set the refname; otherwise try for a sha1 */
 -      if (starts_with((char *)buffer.buf, "ref: ")) {
 -              *symref = xmemdupz((char *)buffer.buf + 5, buffer.len - 6);
 +      if (skip_prefix(buffer.buf, "ref: ", &name)) {
 +              *symref = xmemdupz(name, buffer.len - (name - buffer.buf));
        } else {
                get_sha1_hex(buffer.buf, sha1);
        }
@@@ -1682,8 -1664,7 +1673,7 @@@ static int delete_remote_branch(const c
        fprintf(stderr, "Removing remote branch '%s'\n", remote_ref->name);
        if (dry_run)
                return 0;
-       url = xmalloc(strlen(repo->url) + strlen(remote_ref->name) + 1);
-       sprintf(url, "%s%s", repo->url, remote_ref->name);
+       url = xstrfmt("%s%s", repo->url, remote_ref->name);
        slot = get_active_slot();
        slot->results = &results;
        curl_setup_http_get(slot->curl, url, DAV_DELETE);
diff --combined merge-recursive.c
index e6e1fa33fdd0d7615c61a00d21111a5a83fe96cd,398a734f3f385bdf1c1a3ea62096527bd17823f3..b5c3c5314f8a4b30dc19b12f311383dd189ae3db
@@@ -40,7 -40,7 +40,7 @@@ static struct tree *shift_tree_object(s
  
  static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
  {
 -      struct commit *commit = xcalloc(1, sizeof(struct commit));
 +      struct commit *commit = alloc_commit_node();
        struct merge_remote_desc *desc = xmalloc(sizeof(*desc));
  
        desc->name = comment;
@@@ -190,11 -190,9 +190,11 @@@ static void output_commit_title(struct 
                        printf(_("(bad commit)\n"));
                else {
                        const char *title;
 -                      int len = find_commit_subject(commit->buffer, &title);
 +                      const char *msg = get_commit_buffer(commit, NULL);
 +                      int len = find_commit_subject(msg, &title);
                        if (len)
                                printf("%.*s\n", len, title);
 +                      unuse_commit_buffer(commit, msg);
                }
        }
  }
@@@ -603,25 -601,36 +603,36 @@@ static int remove_file(struct merge_opt
        return 0;
  }
  
+ /* add a string to a strbuf, but converting "/" to "_" */
+ static void add_flattened_path(struct strbuf *out, const char *s)
+ {
+       size_t i = out->len;
+       strbuf_addstr(out, s);
+       for (; i < out->len; i++)
+               if (out->buf[i] == '/')
+                       out->buf[i] = '_';
+ }
  static char *unique_path(struct merge_options *o, const char *path, const char *branch)
  {
-       char *newpath = xmalloc(strlen(path) + 1 + strlen(branch) + 8 + 1);
+       struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
        struct stat st;
-       char *p = newpath + strlen(path);
-       strcpy(newpath, path);
-       *(p++) = '~';
-       strcpy(p, branch);
-       for (; *p; ++p)
-               if ('/' == *p)
-                       *p = '_';
-       while (string_list_has_string(&o->current_file_set, newpath) ||
-              string_list_has_string(&o->current_directory_set, newpath) ||
-              lstat(newpath, &st) == 0)
-               sprintf(p, "_%d", suffix++);
-       string_list_insert(&o->current_file_set, newpath);
-       return newpath;
+       size_t base_len;
+       strbuf_addf(&newpath, "%s~", path);
+       add_flattened_path(&newpath, branch);
+       base_len = newpath.len;
+       while (string_list_has_string(&o->current_file_set, newpath.buf) ||
+              string_list_has_string(&o->current_directory_set, newpath.buf) ||
+              lstat(newpath.buf, &st) == 0) {
+               strbuf_setlen(&newpath, base_len);
+               strbuf_addf(&newpath, "_%d", suffix++);
+       }
+       string_list_insert(&o->current_file_set, newpath.buf);
+       return strbuf_detach(&newpath, NULL);
  }
  
  static int dir_in_way(const char *path, int check_working_copy)
@@@ -971,14 -980,10 +982,10 @@@ merge_file_special_markers(struct merge
        char *side2 = NULL;
        struct merge_file_info mfi;
  
-       if (filename1) {
-               side1 = xmalloc(strlen(branch1) + strlen(filename1) + 2);
-               sprintf(side1, "%s:%s", branch1, filename1);
-       }
-       if (filename2) {
-               side2 = xmalloc(strlen(branch2) + strlen(filename2) + 2);
-               sprintf(side2, "%s:%s", branch2, filename2);
-       }
+       if (filename1)
+               side1 = xstrfmt("%s:%s", branch1, filename1);
+       if (filename2)
+               side2 = xstrfmt("%s:%s", branch2, filename2);
  
        mfi = merge_file_1(o, one, a, b,
                           side1 ? side1 : branch1, side2 ? side2 : branch2);
@@@ -2065,8 -2070,6 +2072,8 @@@ void init_merge_options(struct merge_op
  
  int parse_merge_opt(struct merge_options *o, const char *s)
  {
 +      const char *arg;
 +
        if (!s || !*s)
                return -1;
        if (!strcmp(s, "ours"))
                o->recursive_variant = MERGE_RECURSIVE_THEIRS;
        else if (!strcmp(s, "subtree"))
                o->subtree_shift = "";
 -      else if (starts_with(s, "subtree="))
 -              o->subtree_shift = s + strlen("subtree=");
 +      else if (skip_prefix(s, "subtree=", &arg))
 +              o->subtree_shift = arg;
        else if (!strcmp(s, "patience"))
                o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
                o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
 -      else if (starts_with(s, "diff-algorithm=")) {
 -              long value = parse_algorithm_value(s + strlen("diff-algorithm="));
 +      else if (skip_prefix(s, "diff-algorithm=", &arg)) {
 +              long value = parse_algorithm_value(arg);
                if (value < 0)
                        return -1;
                /* clear out previous settings */
                o->renormalize = 1;
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
 -      else if (starts_with(s, "rename-threshold=")) {
 -              const char *score = s + strlen("rename-threshold=");
 -              if ((o->rename_score = parse_rename_score(&score)) == -1 || *score != 0)
 +      else if (skip_prefix(s, "rename-threshold=", &arg)) {
 +              if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
                        return -1;
        }
        else
diff --combined remote.c
index a0c6ccf5b28e2dfaf4cfb41c22290de550b7cc7d,bf27e44762d35c61a23e664f485ebcea0e935d3f..3d6c86a36f26406305d58505b10db3f747a2546c
+++ b/remote.c
@@@ -170,7 -170,6 +170,6 @@@ static struct branch *make_branch(cons
  {
        struct branch *ret;
        int i;
-       char *refname;
  
        for (i = 0; i < branches_nr; i++) {
                if (len ? (!strncmp(name, branches[i]->name, len) &&
                ret->name = xstrndup(name, len);
        else
                ret->name = xstrdup(name);
-       refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
-       strcpy(refname, "refs/heads/");
-       strcpy(refname + strlen("refs/heads/"), ret->name);
-       ret->refname = refname;
+       ret->refname = xstrfmt("refs/heads/%s", ret->name);
  
        return ret;
  }
@@@ -488,8 -484,9 +484,8 @@@ static void read_config(void
        current_branch = NULL;
        head_ref = resolve_ref_unsafe("HEAD", sha1, 0, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
 -          starts_with(head_ref, "refs/heads/")) {
 -              current_branch =
 -                      make_branch(head_ref + strlen("refs/heads/"), 0);
 +          skip_prefix(head_ref, "refs/heads/", &head_ref)) {
 +              current_branch = make_branch(head_ref, 0);
        }
        git_config(handle_config, NULL);
        if (branch_pushremote_name) {
@@@ -1193,7 -1190,7 +1189,7 @@@ static int match_explicit(struct ref *s
        case 1:
                break;
        case 0:
 -              if (!memcmp(dst_value, "refs/", 5))
 +              if (starts_with(dst_value, "refs/"))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
                else if (is_null_sha1(matched_src->new_sha1))
                        error("unable to delete '%s': remote ref does not exist",
diff --combined sequencer.c
index c513d7eeb4d747cbc41950e3ff197aca8d81d6d4,2fea8243491b5dcabf525ef70c6205aba0e943f5..cdd30c07379621561edf7b7fb23c5607571edf4f
@@@ -116,23 -116,39 +116,23 @@@ static const char *action_name(const st
        return opts->action == REPLAY_REVERT ? "revert" : "cherry-pick";
  }
  
 -static char *get_encoding(const char *message);
 -
  struct commit_message {
        char *parent_label;
        const char *label;
        const char *subject;
 -      char *reencoded_message;
        const char *message;
  };
  
  static int get_message(struct commit *commit, struct commit_message *out)
  {
 -      const char *encoding;
        const char *abbrev, *subject;
        int abbrev_len, subject_len;
        char *q;
  
 -      if (!commit->buffer)
 -              return -1;
 -      encoding = get_encoding(commit->buffer);
 -      if (!encoding)
 -              encoding = "UTF-8";
        if (!git_commit_encoding)
                git_commit_encoding = "UTF-8";
  
 -      out->reencoded_message = NULL;
 -      out->message = commit->buffer;
 -      if (same_encoding(encoding, git_commit_encoding))
 -              out->reencoded_message = reencode_string(commit->buffer,
 -                                      git_commit_encoding, encoding);
 -      if (out->reencoded_message)
 -              out->message = out->reencoded_message;
 -
 +      out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
  
        return 0;
  }
  
 -static void free_message(struct commit_message *msg)
 +static void free_message(struct commit *commit, struct commit_message *msg)
  {
        free(msg->parent_label);
 -      free(msg->reencoded_message);
 -}
 -
 -static char *get_encoding(const char *message)
 -{
 -      const char *p = message, *eol;
 -
 -      while (*p && *p != '\n') {
 -              for (eol = p + 1; *eol && *eol != '\n'; eol++)
 -                      ; /* do nothing */
 -              if (starts_with(p, "encoding ")) {
 -                      char *result = xmalloc(eol - 8 - p);
 -                      strlcpy(result, p + 9, eol - 8 - p);
 -                      return result;
 -              }
 -              p = eol;
 -              if (*p == '\n')
 -                      p++;
 -      }
 -      return NULL;
 +      unuse_commit_buffer(commit, msg->message);
  }
  
  static void write_cherry_pick_head(struct commit *commit, const char *pseudoref)
@@@ -243,7 -278,7 +243,7 @@@ static int fast_forward_to(const unsign
  
        read_cache();
        if (checkout_fast_forward(from, to, 1))
 -              exit(1); /* the callee should have complained already */
 +              exit(128); /* the callee should have complained already */
        ref_lock = lock_any_ref_for_update("HEAD", unborn ? null_sha1 : from,
                                           0, NULL);
        if (!ref_lock)
@@@ -361,18 -396,13 +361,13 @@@ static int run_git_commit(const char *d
  {
        struct argv_array array;
        int rc;
-       char *gpg_sign;
  
        argv_array_init(&array);
        argv_array_push(&array, "commit");
        argv_array_push(&array, "-n");
  
-       if (opts->gpg_sign) {
-               gpg_sign = xmalloc(3 + strlen(opts->gpg_sign));
-               sprintf(gpg_sign, "-S%s", opts->gpg_sign);
-               argv_array_push(&array, gpg_sign);
-               free(gpg_sign);
-       }
+       if (opts->gpg_sign)
+               argv_array_pushf(&array, "-S%s", opts->gpg_sign);
        if (opts->signoff)
                argv_array_push(&array, "-s");
        if (!opts->edit) {
@@@ -454,7 -484,7 +449,7 @@@ static int do_pick_commit(struct commi
        unsigned char head[20];
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
 -      struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
 +      struct commit_message msg = { NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
        int res, unborn = 0, allow;
                res = run_git_commit(defmsg, opts, allow);
  
  leave:
 -      free_message(&msg);
 +      free_message(commit, &msg);
        free(defmsg);
  
        return res;
@@@ -666,12 -696,10 +661,12 @@@ static int format_todo(struct strbuf *b
        int subject_len;
  
        for (cur = todo_list; cur; cur = cur->next) {
 +              const char *commit_buffer = get_commit_buffer(cur->item, NULL);
                sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
 -              subject_len = find_commit_subject(cur->item->buffer, &subject);
 +              subject_len = find_commit_subject(commit_buffer, &subject);
                strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
                        subject_len, subject);
 +              unuse_commit_buffer(cur->item, commit_buffer);
        }
        return 0;
  }
diff --combined sha1_name.c
index a91e0c924b8d3b40a280de92fdded2a04a961221,5e956904b6684bca0cca4e50c71e2d94ce1b8ba3..5bfa8416999d35d6bb5c615d86763f013b7df748
@@@ -862,17 -862,27 +862,17 @@@ static int get_sha1_oneline(const char 
                commit_list_insert(l->item, &backup);
        }
        while (list) {
 -              char *p, *to_free = NULL;
 +              const char *p, *buf;
                struct commit *commit;
 -              enum object_type type;
 -              unsigned long size;
                int matches;
  
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
                if (!parse_object(commit->object.sha1))
                        continue;
 -              if (commit->buffer)
 -                      p = commit->buffer;
 -              else {
 -                      p = read_sha1_file(commit->object.sha1, &type, &size);
 -                      if (!p)
 -                              continue;
 -                      to_free = p;
 -              }
 -
 -              p = strstr(p, "\n\n");
 +              buf = get_commit_buffer(commit, NULL);
 +              p = strstr(buf, "\n\n");
                matches = p && !regexec(&regex, p + 2, 0, NULL, 0);
 -              free(to_free);
 +              unuse_commit_buffer(commit, buf);
  
                if (matches) {
                        hashcpy(sha1, commit->object.sha1);
@@@ -901,8 -911,10 +901,8 @@@ static int grab_nth_branch_switch(unsig
        const char *match = NULL, *target = NULL;
        size_t len;
  
 -      if (starts_with(message, "checkout: moving from ")) {
 -              match = message + strlen("checkout: moving from ");
 +      if (skip_prefix(message, "checkout: moving from ", &match))
                target = strstr(match, " to ");
 -      }
  
        if (!match || !target)
                return 0;
@@@ -1240,10 -1252,7 +1240,7 @@@ static void diagnose_invalid_sha1_path(
                die("Path '%s' exists on disk, but not in '%.*s'.",
                    filename, object_name_len, object_name);
        if (errno == ENOENT || errno == ENOTDIR) {
-               char *fullname = xmalloc(strlen(filename)
-                                            + strlen(prefix) + 1);
-               strcpy(fullname, prefix);
-               strcat(fullname, filename);
+               char *fullname = xstrfmt("%s%s", prefix, filename);
  
                if (!get_tree_entry(tree_sha1, fullname,
                                    sha1, &mode)) {
diff --combined unpack-trees.c
index e9a05b908589a0fa8c446b1896821b77aa24596c,c237370b1f705e2e4aa4b51b8635dd50e7b8d013..0ac39e93a08733ee46f42dfa1170af7333ae43e5
@@@ -56,17 -56,15 +56,15 @@@ void setup_unpack_trees_porcelain(struc
        int i;
        const char **msgs = opts->msgs;
        const char *msg;
-       char *tmp;
        const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
        if (advice_commit_before_merge)
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
                        "Please, commit your changes or stash them before you can %s.";
        else
                msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
-       sprintf(tmp, msg, cmd, cmd2);
-       msgs[ERROR_WOULD_OVERWRITE] = tmp;
-       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+       msgs[ERROR_WOULD_OVERWRITE] = msgs[ERROR_NOT_UPTODATE_FILE] =
+               xstrfmt(msg, cmd, cmd2);
  
        msgs[ERROR_NOT_UPTODATE_DIR] =
                "Updating the following directories would lose untracked files in it:\n%s";
                        "Please move or remove them before you can %s.";
        else
                msg = "The following untracked working tree files would be %s by %s:\n%%s";
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "removed", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
-       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
-       sprintf(tmp, msg, "overwritten", cmd, cmd2);
-       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = xstrfmt(msg, "removed", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = xstrfmt(msg, "overwritten", cmd, cmd2);
  
        /*
         * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
@@@ -622,6 -617,17 +617,6 @@@ static int unpack_failed(struct unpack_
        return -1;
  }
  
 -/* NEEDSWORK: give this a better name and share with tree-walk.c */
 -static int name_compare(const char *a, int a_len,
 -                      const char *b, int b_len)
 -{
 -      int len = (a_len < b_len) ? a_len : b_len;
 -      int cmp = memcmp(a, b, len);
 -      if (cmp)
 -              return cmp;
 -      return (a_len - b_len);
 -}
 -
  /*
   * The tree traversal is looking at name p.  If we have a matching entry,
   * return it.  If name p is a directory in the index, do not return