Merge branch 'mh/find-uniq-abbrev' into maint
authorJunio C Hamano <gitster@pobox.com>
Mon, 12 Jan 2015 22:02:05 +0000 (14:02 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 12 Jan 2015 22:02:05 +0000 (14:02 -0800)
* mh/find-uniq-abbrev:
sha1_name: avoid unnecessary sha1 lookup in find_unique_abbrev

1  2 
sha1_name.c
diff --combined sha1_name.c
index 5b004f513b999b31b7968b22dde955c859704853,e98d0304683aa82c467714e62f2ccd24ca788d9d..cb88170252a26c37d90f5c03263136439ec60e54
@@@ -343,6 -343,7 +343,6 @@@ static int get_short_sha1(const char *n
        return status;
  }
  
 -
  int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
  {
        char hex_pfx[40];
@@@ -372,10 -373,10 +372,10 @@@ const char *find_unique_abbrev(const un
        int status, exists;
        static char hex[41];
  
-       exists = has_sha1_file(sha1);
        memcpy(hex, sha1_to_hex(sha1), 40);
        if (len == 40 || !len)
                return hex;
+       exists = has_sha1_file(sha1);
        while (len < 40) {
                unsigned char sha1_ret[20];
                status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
@@@ -430,10 -431,9 +430,10 @@@ static inline int upstream_mark(const c
  }
  
  static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
 -static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf);
 +static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
  
 -static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 +static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
 +                        unsigned int flags)
  {
        static const char *warn_msg = "refname '%.*s' is ambiguous.";
        static const char *object_name_msg = N_(
        int at, reflog_len, nth_prior = 0;
  
        if (len == 40 && !get_sha1_hex(str, sha1)) {
 -              if (warn_on_object_refname_ambiguity) {
 +              if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
                        refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
 -                      if (refs_found > 0 && warn_ambiguous_refs) {
 +                      if (refs_found > 0) {
                                warning(warn_msg, len, str);
                                if (advice_object_name_warning)
                                        fprintf(stderr, "%s\n", _(object_name_msg));
                struct strbuf buf = STRBUF_INIT;
                int detached;
  
 -              if (interpret_nth_prior_checkout(str, &buf) > 0) {
 +              if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
                        detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
                        strbuf_release(&buf);
                        if (detached)
        if (!refs_found)
                return -1;
  
 -      if (warn_ambiguous_refs &&
 +      if (warn_ambiguous_refs && !(flags & GET_SHA1_QUIETLY) &&
            (refs_found > 1 ||
             !get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
                warning(warn_msg, len, str);
                        char *tmp = xstrndup(str + at + 2, reflog_len);
                        at_time = approxidate_careful(tmp, &errors);
                        free(tmp);
 -                      if (errors)
 +                      if (errors) {
 +                              free(real_ref);
                                return -1;
 +                      }
                }
 -              if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
 +              if (read_ref_at(real_ref, flags, at_time, nth, sha1, NULL,
                                &co_time, &co_tz, &co_cnt)) {
                        if (!len) {
 -                              if (!prefixcmp(real_ref, "refs/heads/")) {
 +                              if (starts_with(real_ref, "refs/heads/")) {
                                        str = real_ref + 11;
                                        len = strlen(real_ref + 11);
                                } else {
                                        len = 4;
                                }
                        }
 -                      if (at_time)
 -                              warning("Log for '%.*s' only goes "
 -                                      "back to %s.", len, str,
 -                                      show_date(co_time, co_tz, DATE_RFC2822));
 -                      else {
 +                      if (at_time) {
 +                              if (!(flags & GET_SHA1_QUIETLY)) {
 +                                      warning("Log for '%.*s' only goes "
 +                                              "back to %s.", len, str,
 +                                              show_date(co_time, co_tz, DATE_RFC2822));
 +                              }
 +                      } else {
 +                              if (flags & GET_SHA1_QUIETLY) {
 +                                      exit(128);
 +                              }
                                die("Log for '%.*s' only has %d entries.",
                                    len, str, co_cnt);
                        }
@@@ -589,6 -582,8 +589,6 @@@ static int get_parent(const char *name
        if (ret)
                return ret;
        commit = lookup_commit_reference(sha1);
 -      if (!commit)
 -              return -1;
        if (parse_commit(commit))
                return -1;
        if (!idx) {
@@@ -682,15 -677,13 +682,15 @@@ static int peel_onion(const char *name
                return -1;
  
        sp++; /* beginning of type name, or closing brace for empty */
 -      if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
 +      if (starts_with(sp, "commit}"))
                expected_type = OBJ_COMMIT;
 -      else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
 +      else if (starts_with(sp, "tag}"))
 +              expected_type = OBJ_TAG;
 +      else if (starts_with(sp, "tree}"))
                expected_type = OBJ_TREE;
 -      else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
 +      else if (starts_with(sp, "blob}"))
                expected_type = OBJ_BLOB;
 -      else if (!prefixcmp(sp, "object}"))
 +      else if (starts_with(sp, "object}"))
                expected_type = OBJ_ANY;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
@@@ -807,7 -800,7 +807,7 @@@ static int get_sha1_1(const char *name
        if (!ret)
                return 0;
  
 -      ret = get_sha1_basic(name, len, sha1);
 +      ret = get_sha1_basic(name, len, sha1, lookup_flags);
        if (!ret)
                return 0;
  
   * For future extension, ':/!' is reserved. If you want to match a message
   * beginning with a '!', you have to repeat the exclamation mark.
   */
 +
 +/* Remember to update object flag allocation in object.h */
  #define ONELINE_SEEN (1u<<20)
  
  static int handle_one_ref(const char *path,
        }
        if (object->type != OBJ_COMMIT)
                return 0;
 -      commit_list_insert_by_date((struct commit *)object, list);
 +      commit_list_insert((struct commit *)object, list);
        return 0;
  }
  
@@@ -870,17 -861,27 +870,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);
@@@ -909,8 -910,10 +909,8 @@@ static int grab_nth_branch_switch(unsig
        const char *match = NULL, *target = NULL;
        size_t len;
  
 -      if (!prefixcmp(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;
   * Parse @{-N} syntax, return the number of characters parsed
   * if successful; otherwise signal an error with negative value.
   */
 -static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
 +static int interpret_nth_prior_checkout(const char *name, int namelen,
 +                                      struct strbuf *buf)
  {
        long nth;
        int retval;
        const char *brace;
        char *num_end;
  
 +      if (namelen < 4)
 +              return -1;
        if (name[0] != '@' || name[1] != '{' || name[2] != '-')
                return -1;
 -      brace = strchr(name, '}');
 +      brace = memchr(name, '}', namelen);
        if (!brace)
                return -1;
        nth = strtol(name + 3, &num_end, 10);
        retval = 0;
        if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
                strbuf_reset(buf);
 -              strbuf_add(buf, cb.buf.buf, cb.buf.len);
 +              strbuf_addbuf(buf, &cb.buf);
                retval = brace - name + 1;
        }
  
@@@ -1004,28 -1004,6 +1004,28 @@@ int get_sha1_mb(const char *name, unsig
        return st;
  }
  
 +/* parse @something syntax, when 'something' is not {.*} */
 +static int interpret_empty_at(const char *name, int namelen, int len, struct strbuf *buf)
 +{
 +      const char *next;
 +
 +      if (len || name[1] == '{')
 +              return -1;
 +
 +      /* make sure it's a single @, or @@{.*}, not @foo */
 +      next = memchr(name + len + 1, '@', namelen - len - 1);
 +      if (next && next[1] != '{')
 +              return -1;
 +      if (!next)
 +              next = name + namelen;
 +      if (next != name + 1)
 +              return -1;
 +
 +      strbuf_reset(buf);
 +      strbuf_add(buf, "HEAD", 4);
 +      return 1;
 +}
 +
  static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
  {
        /* we have extra data, which might need further processing */
        int ret;
  
        strbuf_add(buf, name + len, namelen - len);
 -      ret = interpret_branch_name(buf->buf, &tmp);
 +      ret = interpret_branch_name(buf->buf, buf->len, &tmp);
        /* that data was not interpreted, remove our cruft */
        if (ret < 0) {
                strbuf_setlen(buf, used);
        return ret - used + len;
  }
  
 +static void set_shortened_ref(struct strbuf *buf, const char *ref)
 +{
 +      char *s = shorten_unambiguous_ref(ref, 0);
 +      strbuf_reset(buf);
 +      strbuf_addstr(buf, s);
 +      free(s);
 +}
 +
 +static const char *get_upstream_branch(const char *branch_buf, int len)
 +{
 +      char *branch = xstrndup(branch_buf, len);
 +      struct branch *upstream = branch_get(*branch ? branch : NULL);
 +
 +      /*
 +       * Upstream can be NULL only if branch refers to HEAD and HEAD
 +       * points to something different than a branch.
 +       */
 +      if (!upstream)
 +              die(_("HEAD does not point to a branch"));
 +      if (!upstream->merge || !upstream->merge[0]->dst) {
 +              if (!ref_exists(upstream->refname))
 +                      die(_("No such branch: '%s'"), branch);
 +              if (!upstream->merge) {
 +                      die(_("No upstream configured for branch '%s'"),
 +                              upstream->name);
 +              }
 +              die(
 +                      _("Upstream branch '%s' not stored as a remote-tracking branch"),
 +                      upstream->merge[0]->src);
 +      }
 +      free(branch);
 +
 +      return upstream->merge[0]->dst;
 +}
 +
 +static int interpret_upstream_mark(const char *name, int namelen,
 +                                 int at, struct strbuf *buf)
 +{
 +      int len;
 +
 +      len = upstream_mark(name + at, namelen - at);
 +      if (!len)
 +              return -1;
 +
 +      if (memchr(name, ':', at))
 +              return -1;
 +
 +      set_shortened_ref(buf, get_upstream_branch(name, at));
 +      return len + at;
 +}
 +
  /*
   * This reads short-hand syntax that not only evaluates to a commit
   * object name, but also can act as if the end user spelled the name
   * If the input was ok but there are not N branch switches in the
   * reflog, it returns 0.
   */
 -int interpret_branch_name(const char *name, struct strbuf *buf)
 +int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
  {
 -      char *cp;
 -      struct branch *upstream;
 -      int namelen = strlen(name);
 -      int len = interpret_nth_prior_checkout(name, buf);
 -      int tmp_len;
 +      char *at;
 +      const char *start;
 +      int len = interpret_nth_prior_checkout(name, namelen, buf);
 +
 +      if (!namelen)
 +              namelen = strlen(name);
  
        if (!len) {
                return len; /* syntax Ok, not enough switches */
                        return reinterpret(name, namelen, len, buf);
        }
  
 -      cp = strchr(name, '@');
 -      if (!cp)
 -              return -1;
 -      tmp_len = upstream_mark(cp, namelen - (cp - name));
 -      if (!tmp_len)
 -              return -1;
 -      len = cp + tmp_len - name;
 -      cp = xstrndup(name, cp - name);
 -      upstream = branch_get(*cp ? cp : NULL);
 -      /*
 -       * Upstream can be NULL only if cp refers to HEAD and HEAD
 -       * points to something different than a branch.
 -       */
 -      if (!upstream)
 -              die(_("HEAD does not point to a branch"));
 -      if (!upstream->merge || !upstream->merge[0]->dst) {
 -              if (!ref_exists(upstream->refname))
 -                      die(_("No such branch: '%s'"), cp);
 -              if (!upstream->merge) {
 -                      die(_("No upstream configured for branch '%s'"),
 -                              upstream->name);
 -              }
 -              die(
 -                      _("Upstream branch '%s' not stored as a remote-tracking branch"),
 -                      upstream->merge[0]->src);
 +      for (start = name;
 +           (at = memchr(start, '@', namelen - (start - name)));
 +           start = at + 1) {
 +
 +              len = interpret_empty_at(name, namelen, at - name, buf);
 +              if (len > 0)
 +                      return reinterpret(name, namelen, len, buf);
 +
 +              len = interpret_upstream_mark(name, namelen, at - name, buf);
 +              if (len > 0)
 +                      return len;
        }
 -      free(cp);
 -      cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
 -      strbuf_reset(buf);
 -      strbuf_addstr(buf, cp);
 -      free(cp);
 -      return len;
 +
 +      return -1;
  }
  
  int strbuf_branchname(struct strbuf *sb, const char *name)
  {
        int len = strlen(name);
 -      int used = interpret_branch_name(name, sb);
 +      int used = interpret_branch_name(name, len, sb);
  
        if (used == len)
                return 0;
@@@ -1186,13 -1130,13 +1186,13 @@@ int get_sha1(const char *name, unsigne
  }
  
  /*
 - * Many callers know that the user meant to name a committish by
 + * Many callers know that the user meant to name a commit-ish by
   * syntactical positions where the object name appears.  Calling this
   * function allows the machinery to disambiguate shorter-than-unique
 - * abbreviated object names between committish and others.
 + * abbreviated object names between commit-ish and others.
   *
   * Note that this does NOT error out when the named object is not a
 - * committish. It is merely to give a hint to the disambiguation
 + * commit-ish. It is merely to give a hint to the disambiguation
   * machinery.
   */
  int get_sha1_committish(const char *name, unsigned char *sha1)
@@@ -1248,7 -1192,10 +1248,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)) {
@@@ -1326,7 -1273,7 +1326,7 @@@ static void diagnose_invalid_index_path
  
  static char *resolve_relative_path(const char *rel)
  {
 -      if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
 +      if (!starts_with(rel, "./") && !starts_with(rel, "../"))
                return NULL;
  
        if (!startup_info)
@@@ -1372,7 -1319,6 +1372,7 @@@ static int get_sha1_with_context_1(cons
                if (!only_to_die && namelen > 2 && name[1] == '/') {
                        struct commit_list *list = NULL;
                        for_each_ref(handle_one_ref, &list);
 +                      commit_list_sort_by_date(&list);
                        return get_sha1_oneline(name + 2, sha1, list);
                }
                if (namelen < 3 ||