Merge branch 'jk/skip-prefix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 9 Jul 2014 18:33:27 +0000 (11:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Jul 2014 18:33:28 +0000 (11:33 -0700)
* jk/skip-prefix:
http-push: refactor parsing of remote object names
imap-send: use skip_prefix instead of using magic numbers
use skip_prefix to avoid repeated calculations
git: avoid magic number with skip_prefix
fetch-pack: refactor parsing in get_ack
fast-import: refactor parsing of spaces
stat_opt: check extra strlen call
daemon: use skip_prefix to avoid magic numbers
fast-import: use skip_prefix for parsing input
use skip_prefix to avoid repeating strings
use skip_prefix to avoid magic numbers
transport-helper: avoid reading past end-of-string
fast-import: fix read of uninitialized argv memory
apply: use skip_prefix instead of raw addition
refactor skip_prefix to return a boolean
avoid using skip_prefix as a boolean
daemon: mark some strings as const
parse_diff_color_slot: drop ofs parameter

13 files changed:
1  2 
builtin/commit.c
builtin/fmt-merge-msg.c
builtin/log.c
commit.c
fetch-pack.c
fsck.c
git-compat-util.h
git.c
http.c
merge-recursive.c
pretty.c
remote.c
sha1_name.c
diff --combined builtin/commit.c
index 84cec9a715341fedeabfaedfd82bbb64a4c3c255,ec753412381f26b29274805d32524515171bb39d..461c3b1cade4a995d3eb3cc212645791245865e0
@@@ -1020,7 -1020,7 +1020,7 @@@ static int message_is_empty(struct strb
  static int template_untouched(struct strbuf *sb)
  {
        struct strbuf tmpl = STRBUF_INIT;
-       char *start;
+       const char *start;
  
        if (cleanup_mode == CLEANUP_NONE && sb->len)
                return 0;
                return 0;
  
        stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
-       start = (char *)skip_prefix(sb->buf, tmpl.buf);
-       if (!start)
+       if (!skip_prefix(sb->buf, tmpl.buf, &start))
                start = sb->buf;
        strbuf_release(&tmpl);
        return rest_is_empty(sb, start - sb->buf);
@@@ -1745,8 -1744,8 +1744,8 @@@ int cmd_commit(int argc, const char **a
                append_merge_tag_headers(parents, &tail);
        }
  
 -      if (commit_tree_extended(&sb, active_cache_tree->sha1, parents, sha1,
 -                               author_ident.buf, sign_commit, extra)) {
 +      if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
 +                       parents, sha1, author_ident.buf, sign_commit, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
diff --combined builtin/fmt-merge-msg.c
index ef8b254ef218249f7f1b0f28db7ea21110851917,ad3bc58c74a2b6927d3604e458e01a14b9834946..971e802c6f2945cb6218b7a98c9a30fdc0c9f94b
@@@ -100,7 -100,8 +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 {
@@@ -230,14 -230,12 +230,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 -297,8 +300,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 builtin/log.c
index 92063dfc48a73c9b5fa4f9694dd4ac78d5575be3,0f59c25d362d8238786a884f0a154f29bd97d767..27c1b65db46bb1bead9045b7b6be08a1ee199835
@@@ -347,7 -347,8 +347,7 @@@ static int cmd_log_walk(struct rev_inf
                        rev->max_count++;
                if (!rev->reflog_info) {
                        /* we allow cycles in reflog ancestry */
 -                      free(commit->buffer);
 -                      commit->buffer = NULL;
 +                      free_commit_buffer(commit);
                }
                free_commit_list(commit->parents);
                commit->parents = NULL;
@@@ -871,7 -872,7 +871,7 @@@ static char *find_branch_name(struct re
        int i, positive = -1;
        unsigned char branch_sha1[20];
        const unsigned char *tip_sha1;
-       const char *ref;
+       const char *ref, *v;
        char *full_ref, *branch = NULL;
  
        for (i = 0; i < rev->cmdline.nr; i++) {
        ref = rev->cmdline.rev[positive].name;
        tip_sha1 = rev->cmdline.rev[positive].item->sha1;
        if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
-           starts_with(full_ref, "refs/heads/") &&
+           skip_prefix(full_ref, "refs/heads/", &v) &&
            !hashcmp(tip_sha1, branch_sha1))
-               branch = xstrdup(full_ref + strlen("refs/heads/"));
+               branch = xstrdup(v);
        free(full_ref);
        return branch;
  }
@@@ -924,12 -925,9 +924,12 @@@ static void make_cover_letter(struct re
        log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
                                &need_8bit_cte);
  
 -      for (i = 0; !need_8bit_cte && i < nr; i++)
 -              if (has_non_ascii(list[i]->buffer))
 +      for (i = 0; !need_8bit_cte && i < nr; i++) {
 +              const char *buf = get_commit_buffer(list[i], NULL);
 +              if (has_non_ascii(buf))
                        need_8bit_cte = 1;
 +              unuse_commit_buffer(list[i], buf);
 +      }
  
        if (!branch_name)
                branch_name = find_branch_name(rev);
@@@ -1396,10 -1394,10 +1396,10 @@@ int cmd_format_patch(int argc, const ch
  
                if (check_head) {
                        unsigned char sha1[20];
-                       const char *ref;
+                       const char *ref, *v;
                        ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
-                       if (ref && starts_with(ref, "refs/heads/"))
-                               branch_name = xstrdup(ref + strlen("refs/heads/"));
+                       if (ref && skip_prefix(ref, "refs/heads/", &v))
+                               branch_name = xstrdup(v);
                        else
                                branch_name = xstrdup(""); /* no branch */
                }
                    reopen_stdout(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
                        die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
 -              free(commit->buffer);
 -              commit->buffer = NULL;
 +              free_commit_buffer(commit);
  
                /* We put one extra blank line between formatted
                 * patches and this flag is used by log-tree code
diff --combined commit.c
index 4ff8077dbfbc6cdd6c14728a3d33a78d075c236f,dfc0eb02415edccba6195b4d2925196caa242a1a..fb7897c2a4cef3298c014cbac86081b1d6adc837
+++ b/commit.c
@@@ -17,6 -17,7 +17,6 @@@ static struct commit_extra_header *read
  int save_commit_buffer = 1;
  
  const char *commit_type = "commit";
 -static int commit_count;
  
  static struct commit *check_commit(struct object *obj,
                                   const unsigned char *sha1,
@@@ -63,6 -64,7 +63,6 @@@ struct commit *lookup_commit(const unsi
        struct object *obj = lookup_object(sha1);
        if (!obj) {
                struct commit *c = alloc_commit_node();
 -              c->index = commit_count++;
                return create_object(sha1, OBJ_COMMIT, c);
        }
        if (!obj->type)
@@@ -245,76 -247,6 +245,76 @@@ int unregister_shallow(const unsigned c
        return 0;
  }
  
 +struct commit_buffer {
 +      void *buffer;
 +      unsigned long size;
 +};
 +define_commit_slab(buffer_slab, struct commit_buffer);
 +static struct buffer_slab buffer_slab = COMMIT_SLAB_INIT(1, buffer_slab);
 +
 +void set_commit_buffer(struct commit *commit, void *buffer, unsigned long size)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      v->buffer = buffer;
 +      v->size = size;
 +}
 +
 +const void *get_cached_commit_buffer(const struct commit *commit, unsigned long *sizep)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      if (sizep)
 +              *sizep = v->size;
 +      return v->buffer;
 +}
 +
 +const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
 +{
 +      const void *ret = get_cached_commit_buffer(commit, sizep);
 +      if (!ret) {
 +              enum object_type type;
 +              unsigned long size;
 +              ret = read_sha1_file(commit->object.sha1, &type, &size);
 +              if (!ret)
 +                      die("cannot read commit object %s",
 +                          sha1_to_hex(commit->object.sha1));
 +              if (type != OBJ_COMMIT)
 +                      die("expected commit for %s, got %s",
 +                          sha1_to_hex(commit->object.sha1), typename(type));
 +              if (sizep)
 +                      *sizep = size;
 +      }
 +      return ret;
 +}
 +
 +void unuse_commit_buffer(const struct commit *commit, const void *buffer)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      if (v->buffer != buffer)
 +              free((void *)buffer);
 +}
 +
 +void free_commit_buffer(struct commit *commit)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      free(v->buffer);
 +      v->buffer = NULL;
 +      v->size = 0;
 +}
 +
 +const void *detach_commit_buffer(struct commit *commit, unsigned long *sizep)
 +{
 +      struct commit_buffer *v = buffer_slab_at(&buffer_slab, commit);
 +      void *ret;
 +
 +      ret = v->buffer;
 +      if (sizep)
 +              *sizep = v->size;
 +
 +      v->buffer = NULL;
 +      v->size = 0;
 +      return ret;
 +}
 +
  int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size)
  {
        const char *tail = buffer;
@@@ -392,7 -324,7 +392,7 @@@ int parse_commit(struct commit *item
        }
        ret = parse_commit_buffer(item, buffer, size);
        if (save_commit_buffer && !ret) {
 -              item->buffer = buffer;
 +              set_commit_buffer(item, buffer, size);
                return 0;
        }
        free(buffer);
@@@ -607,15 -539,24 +607,14 @@@ static void record_author_date(struct a
                               struct commit *commit)
  {
        const char *buf, *line_end, *ident_line;
 -      char *buffer = NULL;
 +      const char *buffer = get_commit_buffer(commit, NULL);
        struct ident_split ident;
        char *date_end;
        unsigned long date;
  
 -      if (!commit->buffer) {
 -              unsigned long size;
 -              enum object_type type;
 -              buffer = read_sha1_file(commit->object.sha1, &type, &size);
 -              if (!buffer)
 -                      return;
 -      }
 -
 -      for (buf = commit->buffer ? commit->buffer : buffer;
 -           buf;
 -           buf = line_end + 1) {
 +      for (buf = buffer; buf; buf = line_end + 1) {
                line_end = strchrnul(buf, '\n');
-               ident_line = skip_prefix(buf, "author ");
-               if (!ident_line) {
+               if (!skip_prefix(buf, "author ", &ident_line)) {
                        if (!line_end[0] || line_end[1] == '\n')
                                return; /* end of header */
                        continue;
        *(author_date_slab_at(author_date, commit)) = date;
  
  fail_exit:
 -      free(buffer);
 +      unuse_commit_buffer(commit, buffer);
  }
  
  static int compare_commits_by_author_date(const void *a_, const void *b_,
@@@ -1138,14 -1079,17 +1137,14 @@@ static int do_sign_commit(struct strbu
        return 0;
  }
  
 -int parse_signed_commit(const unsigned char *sha1,
 +int parse_signed_commit(const struct commit *commit,
                        struct strbuf *payload, struct strbuf *signature)
  {
 +
        unsigned long size;
 -      enum object_type type;
 -      char *buffer = read_sha1_file(sha1, &type, &size);
 +      const char *buffer = get_commit_buffer(commit, &size);
        int in_signature, saw_signature = -1;
 -      char *line, *tail;
 -
 -      if (!buffer || type != OBJ_COMMIT)
 -              goto cleanup;
 +      const char *line, *tail;
  
        line = buffer;
        tail = buffer + size;
        saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
 -              char *next = memchr(line, '\n', tail - line);
 +              const char *next = memchr(line, '\n', tail - line);
  
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                }
                line = next;
        }
 - cleanup:
 -      free(buffer);
 +      unuse_commit_buffer(commit, buffer);
        return saw_signature;
  }
  
@@@ -1237,8 -1182,7 +1236,7 @@@ static void parse_gpg_output(struct sig
        for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                const char *found, *next;
  
-               found = skip_prefix(buf, sigcheck_gpg_status[i].check + 1);
-               if (!found) {
+               if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
                        found = strstr(buf, sigcheck_gpg_status[i].check);
                        if (!found)
                                continue;
@@@ -1265,7 -1209,8 +1263,7 @@@ void check_commit_signature(const struc
  
        sigc->result = 'N';
  
 -      if (parse_signed_commit(commit->object.sha1,
 -                              &payload, &signature) <= 0)
 +      if (parse_signed_commit(commit, &payload, &signature) <= 0)
                goto out;
        status = verify_signed_buffer(payload.buf, payload.len,
                                      signature.buf, signature.len,
@@@ -1310,9 -1255,11 +1308,9 @@@ struct commit_extra_header *read_commit
  {
        struct commit_extra_header *extra = NULL;
        unsigned long size;
 -      enum object_type type;
 -      char *buffer = read_sha1_file(commit->object.sha1, &type, &size);
 -      if (buffer && type == OBJ_COMMIT)
 -              extra = read_commit_extra_header_lines(buffer, size, exclude);
 -      free(buffer);
 +      const char *buffer = get_commit_buffer(commit, &size);
 +      extra = read_commit_extra_header_lines(buffer, size, exclude);
 +      unuse_commit_buffer(commit, buffer);
        return extra;
  }
  
@@@ -1395,8 -1342,7 +1393,8 @@@ void free_commit_extra_headers(struct c
        }
  }
  
 -int commit_tree(const struct strbuf *msg, const unsigned char *tree,
 +int commit_tree(const char *msg, size_t msg_len,
 +              const unsigned char *tree,
                struct commit_list *parents, unsigned char *ret,
                const char *author, const char *sign_commit)
  {
        int result;
  
        append_merge_tag_headers(parents, &tail);
 -      result = commit_tree_extended(msg, tree, parents, ret,
 +      result = commit_tree_extended(msg, msg_len, tree, parents, ret,
                                      author, sign_commit, extra);
        free_commit_extra_headers(extra);
        return result;
@@@ -1525,8 -1471,7 +1523,8 @@@ static const char commit_utf8_warn[] 
  "You may want to amend it after fixing the message, or set the config\n"
  "variable i18n.commitencoding to the encoding your project uses.\n";
  
 -int commit_tree_extended(const struct strbuf *msg, const unsigned char *tree,
 +int commit_tree_extended(const char *msg, size_t msg_len,
 +                       const unsigned char *tree,
                         struct commit_list *parents, unsigned char *ret,
                         const char *author, const char *sign_commit,
                         struct commit_extra_header *extra)
  
        assert_sha1_type(tree, OBJ_TREE);
  
 -      if (memchr(msg->buf, '\0', msg->len))
 +      if (memchr(msg, '\0', msg_len))
                return error("a NUL byte in commit log message not allowed.");
  
        /* Not having i18n.commitencoding is the same as having utf-8 */
        strbuf_addch(&buffer, '\n');
  
        /* And add the comment */
 -      strbuf_addbuf(&buffer, msg);
 +      strbuf_add(&buffer, msg, msg_len);
  
        /* And check the encoding */
        if (encoding_is_utf8 && !verify_utf8(&buffer))
diff --combined fetch-pack.c
index b12bd4c59a1105f4d3a82bbbd5208e3f7ebae0bb,72ec520fda7124e49d41e03ea81310a231841a24..b8a58fa7a542fe166ad71c9b17a4908c81d24a4f
@@@ -189,20 -189,23 +189,23 @@@ static enum ack_type get_ack(int fd, un
  {
        int len;
        char *line = packet_read_line(fd, &len);
+       const char *arg;
  
        if (!len)
                die("git fetch-pack: expected ACK/NAK, got EOF");
        if (!strcmp(line, "NAK"))
                return NAK;
-       if (starts_with(line, "ACK ")) {
-               if (!get_sha1_hex(line+4, result_sha1)) {
-                       if (len < 45)
+       if (skip_prefix(line, "ACK ", &arg)) {
+               if (!get_sha1_hex(arg, result_sha1)) {
+                       arg += 40;
+                       len -= arg - line;
+                       if (len < 1)
                                return ACK;
-                       if (strstr(line+45, "continue"))
+                       if (strstr(arg, "continue"))
                                return ACK_continue;
-                       if (strstr(line+45, "common"))
+                       if (strstr(arg, "common"))
                                return ACK_common;
-                       if (strstr(line+45, "ready"))
+                       if (strstr(arg, "ready"))
                                return ACK_ready;
                        return ACK;
                }
@@@ -319,18 -322,19 +322,19 @@@ static int find_common(struct fetch_pac
  
        if (args->depth > 0) {
                char *line;
+               const char *arg;
                unsigned char sha1[20];
  
                send_request(args, fd[1], &req_buf);
                while ((line = packet_read_line(fd[0], NULL))) {
-                       if (starts_with(line, "shallow ")) {
-                               if (get_sha1_hex(line + 8, sha1))
+                       if (skip_prefix(line, "shallow ", &arg)) {
+                               if (get_sha1_hex(arg, sha1))
                                        die("invalid shallow line: %s", line);
                                register_shallow(sha1);
                                continue;
                        }
-                       if (starts_with(line, "unshallow ")) {
-                               if (get_sha1_hex(line + 10, sha1))
+                       if (skip_prefix(line, "unshallow ", &arg)) {
+                               if (get_sha1_hex(arg, sha1))
                                        die("invalid unshallow line: %s", line);
                                if (!lookup_object(sha1))
                                        die("object not found: %s", line);
@@@ -507,7 -511,7 +511,7 @@@ static void filter_refs(struct fetch_pa
                int keep = 0;
                next = ref->next;
  
 -              if (!memcmp(ref->name, "refs/", 5) &&
 +              if (starts_with(ref->name, "refs/") &&
                    check_refname_format(ref->name, 0))
                        ; /* trash */
                else {
diff --combined fsck.c
index a7233c8d0b20c06f0aebffbec9c11dacb22b6769,bdbea2b995dd5714e8422e18c0bed08ae1a41afa..a4e8593e78c85c244d2dc8bac0cca838c14bbd4a
--- 1/fsck.c
--- 2/fsck.c
+++ b/fsck.c
@@@ -276,23 -276,20 +276,20 @@@ static int fsck_ident(const char **iden
        return 0;
  }
  
 -static int fsck_commit(struct commit *commit, fsck_error error_func)
 +static int fsck_commit_buffer(struct commit *commit, const char *buffer,
 +                            fsck_error error_func)
  {
-       const char *tmp;
 -      const char *buffer = commit->buffer;
        unsigned char tree_sha1[20], sha1[20];
        struct commit_graft *graft;
        int parents = 0;
        int err;
  
-       buffer = skip_prefix(buffer, "tree ");
-       if (!buffer)
+       if (!skip_prefix(buffer, "tree ", &buffer))
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line");
        if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n')
                return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1");
        buffer += 41;
-       while ((tmp = skip_prefix(buffer, "parent "))) {
-               buffer = tmp;
+       while (skip_prefix(buffer, "parent ", &buffer)) {
                if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
                        return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1");
                buffer += 41;
                if (p || parents)
                        return error_func(&commit->object, FSCK_ERROR, "parent objects missing");
        }
-       buffer = skip_prefix(buffer, "author ");
-       if (!buffer)
+       if (!skip_prefix(buffer, "author ", &buffer))
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line");
        err = fsck_ident(&buffer, &commit->object, error_func);
        if (err)
                return err;
-       buffer = skip_prefix(buffer, "committer ");
-       if (!buffer)
+       if (!skip_prefix(buffer, "committer ", &buffer))
                return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'committer' line");
        err = fsck_ident(&buffer, &commit->object, error_func);
        if (err)
        return 0;
  }
  
 +static int fsck_commit(struct commit *commit, fsck_error error_func)
 +{
 +      const char *buffer = get_commit_buffer(commit, NULL);
 +      int ret = fsck_commit_buffer(commit, buffer, error_func);
 +      unuse_commit_buffer(commit, buffer);
 +      return ret;
 +}
 +
  static int fsck_tag(struct tag *tag, fsck_error error_func)
  {
        struct object *tagged = tag->tagged;
diff --combined git-compat-util.h
index 96f55547a379bc1bdc488fa4557360bbfd1ae253,d29e1dff08de9caa2b37eff71bffe7d37225796e..9de31807108322aac327f66822d4929974d7ffd5
@@@ -349,13 -349,32 +349,32 @@@ extern void set_die_is_recursing_routin
  extern int starts_with(const char *str, const char *prefix);
  extern int ends_with(const char *str, const char *suffix);
  
- static inline const char *skip_prefix(const char *str, const char *prefix)
+ /*
+  * If the string "str" begins with the string found in "prefix", return 1.
+  * The "out" parameter is set to "str + strlen(prefix)" (i.e., to the point in
+  * the string right after the prefix).
+  *
+  * Otherwise, return 0 and leave "out" untouched.
+  *
+  * Examples:
+  *
+  *   [extract branch name, fail if not a branch]
+  *   if (!skip_prefix(ref, "refs/heads/", &branch)
+  *    return -1;
+  *
+  *   [skip prefix if present, otherwise use whole string]
+  *   skip_prefix(name, "refs/heads/", &name);
+  */
+ static inline int skip_prefix(const char *str, const char *prefix,
+                             const char **out)
  {
        do {
-               if (!*prefix)
-                       return str;
+               if (!*prefix) {
+                       *out = str;
+                       return 1;
+               }
        } while (*str++ == *prefix++);
-       return NULL;
+       return 0;
  }
  
  #if defined(NO_MMAP) || defined(USE_WIN32_MMAP)
@@@ -685,17 -704,6 +704,17 @@@ void git_qsort(void *base, size_t nmemb
  #endif
  #endif
  
 +#if defined(__GNUC__) && defined(__x86_64__)
 +#include <emmintrin.h>
 +/*
 + * This is the system memory page size; it's used so that we can read
 + * outside the bounds of an allocation without segfaulting.
 + */
 +#ifndef PAGE_SIZE
 +#define PAGE_SIZE 4096
 +#endif
 +#endif
 +
  #ifdef UNRELIABLE_FSTAT
  #define fstat_is_reliable() 0
  #else
diff --combined git.c
index d6b4a5543f77071aff977f6d58e3ab6c5495f19d,1537f00bc7081aa800d390aecaf6d80ab7c546c9..dd54f5734a246d65436c823dd65fb224b240009f
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -20,43 -20,6 +20,43 @@@ const char git_more_info_string[] 
  
  static struct startup_info git_startup_info;
  static int use_pager = -1;
 +static char orig_cwd[PATH_MAX];
 +static const char *env_names[] = {
 +      GIT_DIR_ENVIRONMENT,
 +      GIT_WORK_TREE_ENVIRONMENT,
 +      GIT_IMPLICIT_WORK_TREE_ENVIRONMENT,
 +      GIT_PREFIX_ENVIRONMENT
 +};
 +static char *orig_env[4];
 +static int saved_environment;
 +
 +static void save_env(void)
 +{
 +      int i;
 +      if (saved_environment)
 +              return;
 +      saved_environment = 1;
 +      if (!getcwd(orig_cwd, sizeof(orig_cwd)))
 +              die_errno("cannot getcwd");
 +      for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 +              orig_env[i] = getenv(env_names[i]);
 +              if (orig_env[i])
 +                      orig_env[i] = xstrdup(orig_env[i]);
 +      }
 +}
 +
 +static void restore_env(void)
 +{
 +      int i;
 +      if (*orig_cwd && chdir(orig_cwd))
 +              die_errno("could not move to %s", orig_cwd);
 +      for (i = 0; i < ARRAY_SIZE(env_names); i++) {
 +              if (orig_env[i])
 +                      setenv(env_names[i], orig_env[i], 1);
 +              else
 +                      unsetenv(env_names[i]);
 +      }
 +}
  
  static void commit_pager_choice(void) {
        switch (use_pager) {
@@@ -91,8 -54,7 +91,7 @@@ static int handle_options(const char **
                /*
                 * Check remaining flags.
                 */
-               if (starts_with(cmd, "--exec-path")) {
-                       cmd += 11;
+               if (skip_prefix(cmd, "--exec-path", &cmd)) {
                        if (*cmd == '=')
                                git_set_argv_exec_path(cmd + 1);
                        else {
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (starts_with(cmd, "--git-dir=")) {
-                       setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
+               } else if (skip_prefix(cmd, "--git-dir=", &cmd)) {
+                       setenv(GIT_DIR_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--namespace")) {
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (starts_with(cmd, "--namespace=")) {
-                       setenv(GIT_NAMESPACE_ENVIRONMENT, cmd + 12, 1);
+               } else if (skip_prefix(cmd, "--namespace=", &cmd)) {
+                       setenv(GIT_NAMESPACE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--work-tree")) {
                                *envchanged = 1;
                        (*argv)++;
                        (*argc)--;
-               } else if (starts_with(cmd, "--work-tree=")) {
-                       setenv(GIT_WORK_TREE_ENVIRONMENT, cmd + 12, 1);
+               } else if (skip_prefix(cmd, "--work-tree=", &cmd)) {
+                       setenv(GIT_WORK_TREE_ENVIRONMENT, cmd, 1);
                        if (envchanged)
                                *envchanged = 1;
                } else if (!strcmp(cmd, "--bare")) {
@@@ -309,7 -271,6 +308,7 @@@ static int handle_alias(int *argcp, con
   * RUN_SETUP for reading from the configuration file.
   */
  #define NEED_WORK_TREE                (1<<3)
 +#define NO_SETUP              (1<<4)
  
  struct cmd_struct {
        const char *cmd;
@@@ -390,7 -351,7 +389,7 @@@ static struct cmd_struct commands[] = 
        { "cherry", cmd_cherry, RUN_SETUP },
        { "cherry-pick", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },
        { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
 -      { "clone", cmd_clone },
 +      { "clone", cmd_clone, NO_SETUP },
        { "column", cmd_column, RUN_SETUP_GENTLY },
        { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
        { "commit-tree", cmd_commit_tree, RUN_SETUP },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
 -      { "init", cmd_init_db },
 -      { "init-db", cmd_init_db },
 +      { "init", cmd_init_db, NO_SETUP },
 +      { "init-db", cmd_init_db, NO_SETUP },
        { "log", cmd_log, RUN_SETUP },
        { "ls-files", cmd_ls_files, RUN_SETUP },
        { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
@@@ -522,10 -483,6 +521,10 @@@ static void handle_builtin(int argc, co
                struct cmd_struct *p = commands+i;
                if (strcmp(p->cmd, cmd))
                        continue;
 +              if (saved_environment && (p->option & NO_SETUP)) {
 +                      restore_env();
 +                      break;
 +              }
                exit(run_builtin(p, argc, argv));
        }
  }
@@@ -581,10 -538,7 +580,10 @@@ static int run_argv(int *argcp, const c
                 * of overriding "git log" with "git show" by having
                 * alias.log = show
                 */
 -              if (done_alias || !handle_alias(argcp, argv))
 +              if (done_alias)
 +                      break;
 +              save_env();
 +              if (!handle_alias(argcp, argv))
                        break;
                done_alias = 1;
        }
@@@ -623,8 -577,7 +622,7 @@@ int main(int argc, char **av
         * So we just directly call the builtin handler, and die if
         * that one cannot handle it.
         */
-       if (starts_with(cmd, "git-")) {
-               cmd += 4;
+       if (skip_prefix(cmd, "git-", &cmd)) {
                argv[0] = cmd;
                handle_builtin(argc, argv);
                die("cannot handle %s as a builtin", cmd);
        argc--;
        handle_options(&argv, &argc, NULL);
        if (argc > 0) {
-               if (starts_with(argv[0], "--"))
-                       argv[0] += 2;
+               /* translate --help and --version into commands */
+               skip_prefix(argv[0], "--", &argv[0]);
        } else {
                /* The user didn't specify a command; give them help */
                commit_pager_choice();
diff --combined http.c
index 3a28b219be5ecb270a6ff0422c7a4a5cdda023f0,f04621e61d157478f4100dc710118e1bd8634c70..c8cd50dd0c2b2213a86957cd4b24c03c0d85717d
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -927,7 -927,7 +927,7 @@@ static int extract_param(const char *ra
                return -1;
        raw++;
  
 -      while (*raw && !isspace(*raw))
 +      while (*raw && !isspace(*raw) && *raw != ';')
                strbuf_addch(out, *raw++);
        return 0;
  }
@@@ -971,7 -971,7 +971,7 @@@ static void extract_content_type(struc
  
        strbuf_reset(charset);
        while (*p) {
 -              while (isspace(*p))
 +              while (isspace(*p) || *p == ';')
                        p++;
                if (!extract_param(p, "charset", charset))
                        return;
@@@ -1087,11 -1087,10 +1087,10 @@@ static int update_url_from_redirect(str
        if (!strcmp(asked, got->buf))
                return 0;
  
-       if (!starts_with(asked, base->buf))
+       if (!skip_prefix(asked, base->buf, &tail))
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
  
-       tail = asked + base->len;
        tail_len = strlen(tail);
  
        if (got->len < tail_len ||
diff --combined merge-recursive.c
index d38a3b2eb5bf4b0940a2aa4d5c32f02b9f35cc15,f8480018178c7eb8b88dcd3e1f166dc953bfbe11..e6e1fa33fdd0d7615c61a00d21111a5a83fe96cd
@@@ -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);
                }
        }
  }
@@@ -2065,6 -2063,8 +2065,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 pretty.c
index c93c14b080b8f23fc30fa58b85c9d70f063c7225,f24752a85bc101c117d91b86a0526ca4c1eceee0..b5cf3d52c3228a06689c847a3202bedf95f00dcf
+++ b/pretty.c
@@@ -40,10 -40,9 +40,9 @@@ static int git_pretty_formats_config(co
        const char *fmt;
        int i;
  
-       if (!starts_with(var, "pretty."))
+       if (!skip_prefix(var, "pretty.", &name))
                return 0;
  
-       name = var + strlen("pretty.");
        for (i = 0; i < builtin_formats_len; i++) {
                if (!strcmp(commit_formats[i].name, name))
                        return 0;
@@@ -274,7 -273,7 +273,7 @@@ static void add_rfc822_quoted(struct st
  
  enum rfc2047_type {
        RFC2047_SUBJECT,
 -      RFC2047_ADDRESS,
 +      RFC2047_ADDRESS
  };
  
  static int is_rfc2047_special(char ch, enum rfc2047_type type)
@@@ -606,16 -605,29 +605,16 @@@ static char *replace_encoding_header(ch
        return strbuf_detach(&tmp, NULL);
  }
  
 -char *logmsg_reencode(const struct commit *commit,
 -                    char **commit_encoding,
 -                    const char *output_encoding)
 +const char *logmsg_reencode(const struct commit *commit,
 +                          char **commit_encoding,
 +                          const char *output_encoding)
  {
        static const char *utf8 = "UTF-8";
        const char *use_encoding;
        char *encoding;
 -      char *msg = commit->buffer;
 +      const char *msg = get_commit_buffer(commit, NULL);
        char *out;
  
 -      if (!msg) {
 -              enum object_type type;
 -              unsigned long size;
 -
 -              msg = read_sha1_file(commit->object.sha1, &type, &size);
 -              if (!msg)
 -                      die("Cannot read commit object %s",
 -                          sha1_to_hex(commit->object.sha1));
 -              if (type != OBJ_COMMIT)
 -                      die("Expected commit for '%s', got %s",
 -                          sha1_to_hex(commit->object.sha1), typename(type));
 -      }
 -
        if (!output_encoding || !*output_encoding) {
                if (commit_encoding)
                        *commit_encoding =
                 * Otherwise, we still want to munge the encoding header in the
                 * result, which will be done by modifying the buffer. If we
                 * are using a fresh copy, we can reuse it. But if we are using
 -               * the cached copy from commit->buffer, we need to duplicate it
 -               * to avoid munging commit->buffer.
 +               * the cached copy from get_commit_buffer, we need to duplicate it
 +               * to avoid munging the cached copy.
                 */
 -              out = msg;
 -              if (out == commit->buffer)
 -                      out = xstrdup(out);
 +              if (msg == get_cached_commit_buffer(commit, NULL))
 +                      out = xstrdup(msg);
 +              else
 +                      out = (char *)msg;
        }
        else {
                /*
                 * copy, we can free it.
                 */
                out = reencode_string(msg, output_encoding, use_encoding);
 -              if (out && msg != commit->buffer)
 -                      free(msg);
 +              if (out)
 +                      unuse_commit_buffer(commit, msg);
        }
  
        /*
        return out ? out : msg;
  }
  
 -void logmsg_free(char *msg, const struct commit *commit)
 -{
 -      if (msg != commit->buffer)
 -              free(msg);
 -}
 -
  static int mailmap_name(const char **email, size_t *email_len,
                        const char **name, size_t *name_len)
  {
@@@ -778,7 -795,7 +777,7 @@@ struct format_commit_context 
        struct signature_check signature_check;
        enum flush_type flush_type;
        enum trunc_type truncate;
 -      char *message;
 +      const char *message;
        char *commit_encoding;
        size_t width, indent1, indent2;
        int auto_color;
@@@ -1518,7 -1535,7 +1517,7 @@@ void format_commit_message(const struc
        }
  
        free(context.commit_encoding);
 -      logmsg_free(context.message, commit);
 +      unuse_commit_buffer(commit, context.message);
        free(context.signature_check.gpg_output);
        free(context.signature_check.signer);
  }
@@@ -1687,7 -1704,7 +1686,7 @@@ void pretty_print_commit(struct pretty_
        unsigned long beginning_of_body;
        int indent = 4;
        const char *msg;
 -      char *reencoded;
 +      const char *reencoded;
        const char *encoding;
        int need_8bit_cte = pp->need_8bit_cte;
  
        if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
  
 -      logmsg_free(reencoded, commit);
 +      unuse_commit_buffer(commit, reencoded);
  }
  
  void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
diff --combined remote.c
index ae040432037f7463233c5add8257b17ace365b23,30d2829c2b171f7af8891b5c07a0317c72de1871..a0c6ccf5b28e2dfaf4cfb41c22290de550b7cc7d
+++ b/remote.c
@@@ -488,9 -488,8 +488,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) {
@@@ -1194,7 -1193,7 +1193,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 sha1_name.c
index c2c938c4e161fc6849a87a7a19336712f36f74ff,72e6ac6a6e4f8573676084e443ce8fae038f4f56..a91e0c924b8d3b40a280de92fdded2a04a961221
@@@ -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,10 -911,8 +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;