Merge branch 'jc/sha1-name-object-peeler'
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Apr 2013 16:34:53 +0000 (09:34 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Apr 2013 16:34:54 +0000 (09:34 -0700)
There was no good way to ask "I have a random string that came from
outside world. I want to turn it into a 40-hex object name while
making sure such an object exists". A new peeling suffix ^{object}
can be used for that purpose, together with "rev-parse --verify".

* jc/sha1-name-object-peeler:
peel_onion(): teach $foo^{object} peeler
peel_onion: disambiguate to favor tree-ish when we know we want a tree-ish

1  2 
Documentation/revisions.txt
sha1_name.c
index 314e25da7344dda459ca5b3292aaaabdfc0d6495,e82a4db61c837fd4ab68ffd59df1bdc5c529fa73..1707d451b604e05de69d8a3d9f8dc326079d4742
@@@ -23,7 -23,7 +23,7 @@@ blobs contained in a commit
    A symbolic ref name.  E.g. 'master' typically means the commit
    object referenced by 'refs/heads/master'.  If you
    happen to have both 'heads/master' and 'tags/master', you can
 -  explicitly say 'heads/master' to tell git which one you mean.
 +  explicitly say 'heads/master' to tell Git which one you mean.
    When ambiguous, a '<refname>' is disambiguated by taking the
    first match in the following rules:
  
@@@ -116,6 -116,11 +116,11 @@@ some output processing may assume ref n
    object of that type is found or the object cannot be
    dereferenced anymore (in which case, barf).  '<rev>{caret}0'
    is a short-hand for '<rev>{caret}\{commit\}'.
+ +
+ 'rev{caret}\{object\}' can be used to make sure 'rev' names an
+ object that exists, without requiring 'rev' to be a tag, and
+ without dereferencing 'rev'; because a tag is already an object,
+ it does not have to be dereferenced even once to get to an object.
  
  '<rev>{caret}\{\}', e.g. 'v0.99.8{caret}\{\}'::
    A suffix '{caret}' followed by an empty brace pair
diff --combined sha1_name.c
index 2fbda48e02f167d08aa19210951712b85f40b602,85b6e757411861b298cc6bee446f73ad0e430df8..3820f28ae757cce54a95014629ade4f7feb56efc
@@@ -594,7 -594,7 +594,7 @@@ struct object *peel_to_type(const char 
        while (1) {
                if (!o || (!o->parsed && !parse_object(o->sha1)))
                        return NULL;
-               if (o->type == expected_type)
+               if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
                if (o->type == OBJ_TAG)
                        o = ((struct tag*) o)->tagged;
@@@ -645,6 -645,8 +645,8 @@@ static int peel_onion(const char *name
                expected_type = OBJ_TREE;
        else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
                expected_type = OBJ_BLOB;
+       else if (!prefixcmp(sp, "object}"))
+               expected_type = OBJ_ANY;
        else if (sp[0] == '}')
                expected_type = OBJ_NONE;
        else if (sp[0] == '/')
  
        if (expected_type == OBJ_COMMIT)
                lookup_flags = GET_SHA1_COMMITTISH;
+       else if (expected_type == OBJ_TREE)
+               lookup_flags = GET_SHA1_TREEISH;
  
        if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
                return -1;
@@@ -856,8 -860,8 +860,8 @@@ static int get_sha1_oneline(const char 
  }
  
  struct grab_nth_branch_switch_cbdata {
 -      long cnt, alloc;
 -      struct strbuf *buf;
 +      int remaining;
 +      struct strbuf buf;
  };
  
  static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
        struct grab_nth_branch_switch_cbdata *cb = cb_data;
        const char *match = NULL, *target = NULL;
        size_t len;
 -      int nth;
  
        if (!prefixcmp(message, "checkout: moving from ")) {
                match = message + strlen("checkout: moving from ");
  
        if (!match || !target)
                return 0;
 -
 -      len = target - match;
 -      nth = cb->cnt++ % cb->alloc;
 -      strbuf_reset(&cb->buf[nth]);
 -      strbuf_add(&cb->buf[nth], match, len);
 +      if (--(cb->remaining) == 0) {
 +              len = target - match;
 +              strbuf_reset(&cb->buf);
 +              strbuf_add(&cb->buf, match, len);
 +              return 1; /* we are done */
 +      }
        return 0;
  }
  
  static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
  {
        long nth;
 -      int i, retval;
 +      int retval;
        struct grab_nth_branch_switch_cbdata cb;
        const char *brace;
        char *num_end;
        brace = strchr(name, '}');
        if (!brace)
                return -1;
 -      nth = strtol(name+3, &num_end, 10);
 +      nth = strtol(name + 3, &num_end, 10);
        if (num_end != brace)
                return -1;
        if (nth <= 0)
                return -1;
 -      cb.alloc = nth;
 -      cb.buf = xmalloc(nth * sizeof(struct strbuf));
 -      for (i = 0; i < nth; i++)
 -              strbuf_init(&cb.buf[i], 20);
 -      cb.cnt = 0;
 +      cb.remaining = nth;
 +      strbuf_init(&cb.buf, 20);
 +
        retval = 0;
 -      for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
 -      if (cb.cnt < nth) {
 -              cb.cnt = 0;
 -              for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
 -      }
 -      if (cb.cnt < nth)
 -              goto release_return;
 -      i = cb.cnt % nth;
 -      strbuf_reset(buf);
 -      strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
 -      retval = brace-name+1;
 -
 -release_return:
 -      for (i = 0; i < nth; i++)
 -              strbuf_release(&cb.buf[i]);
 -      free(cb.buf);
 +      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);
 +              retval = brace - name + 1;
 +      }
  
 +      strbuf_release(&cb.buf);
        return retval;
  }