diff options
| author | Junio C Hamano <junkio@cox.net> | 2005-10-13 18:57:40 -0700 | 
|---|---|---|
| committer | Junio C Hamano <junkio@cox.net> | 2005-10-15 11:23:40 -0700 | 
| commit | 5385f52da80be0d01fda45be586eb186c2be83ee (patch) | |
| tree | 631b2396995353f2682599d7f780180add76a3bf | |
| parent | 1a7141ff28e217312da4b289127875905c6ec479 (diff) | |
| download | git-5385f52da80be0d01fda45be586eb186c2be83ee.tar.gz | |
Introduce notation "ref^{type}".
Existing "tagname^0" notation means "dereference tag zero or more
times until you cannot dereference it anymore, and make sure it is a
commit -- otherwise barf".  But tags do not necessarily reference
commit objects.
This commit introduces a bit more generalized notation, "ref^{type}".
Existing "ref^0" is a shorthand for "ref^{commit}".  If the type
is empty, it just dereferences tags until it hits a non-tag object.
With this, "git-rev-parse --verify 'junio-gpg-pub^{}'" shows the blob
object name -- there is no need to manually read the tag object and
find out the object name anymore.
"git-rev-parse --verify 'HEAD^{tree}'" can be used to find out the
tree object name of the HEAD commit.
Signed-off-by: Junio C Hamano <junkio@cox.net>
| -rw-r--r-- | sha1_name.c | 83 | 
1 files changed, 83 insertions, 0 deletions
diff --git a/sha1_name.c b/sha1_name.c index 4e9a052333..75c688ecf2 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -1,5 +1,8 @@  #include "cache.h" +#include "tag.h"  #include "commit.h" +#include "tree.h" +#include "blob.h"  static int find_short_object_filename(int len, const char *name, unsigned char *sha1)  { @@ -274,6 +277,82 @@ static int get_nth_ancestor(const char *name, int len,  	return 0;  } +static int peel_onion(const char *name, int len, unsigned char *sha1) +{ +	unsigned char outer[20]; +	const char *sp; +	const char *type_string = NULL; +	struct object *o; + +	/* +	 * "ref^{type}" dereferences ref repeatedly until you cannot +	 * dereference anymore, or you get an object of given type, +	 * whichever comes first.  "ref^{}" means just dereference +	 * tags until you get a non-tag.  "ref^0" is a shorthand for +	 * "ref^{commit}".  "commit^{tree}" could be used to find the +	 * top-level tree of the given commit. +	 */ +	if (len < 4 || name[len-1] != '}') +		return -1; + +	for (sp = name + len - 1; name <= sp; sp--) { +		int ch = *sp; +		if (ch == '{' && name < sp && sp[-1] == '^') +			break; +	} +	if (sp <= name) +		return -1; + +	sp++; /* beginning of type name, or closing brace for empty */ +	if (!strncmp(commit_type, sp, 6) && sp[6] == '}') +		type_string = commit_type; +	else if (!strncmp(tree_type, sp, 4) && sp[4] == '}') +		type_string = tree_type; +	else if (!strncmp(blob_type, sp, 4) && sp[4] == '}') +		type_string = blob_type; +	else if (sp[0] == '}') +		type_string = NULL; +	else +		return -1; + +	if (get_sha1_1(name, sp - name - 2, outer)) +		return -1; + +	o = parse_object(outer); +	if (!o) +		return -1; +	if (!type_string) { +		o = deref_tag(o); +		memcpy(sha1, o->sha1, 20); +	} +	else { +		/* At this point, the syntax look correct, so +		 * if we do not get the needed object, we should +		 * barf. +		 */ + +		while (1) { +			if (!o) +				return -1; +			if (o->type == type_string) { +				memcpy(sha1, o->sha1, 20); +				return 0; +			} +			if (o->type == tag_type) +				o = ((struct tag*) o)->tagged; +			else if (o->type == commit_type) +				o = &(((struct commit *) o)->tree->object); +			else +				return error("%.*s: expected %s type, but the object dereferences to %s type", +					     len, name, type_string, +					     o->type); +			if (!o->parsed) +				parse_object(o->sha1); +		} +	} +	return 0; +} +  static int get_sha1_1(const char *name, int len, unsigned char *sha1)  {  	int parent, ret; @@ -315,6 +394,10 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)  		return get_nth_ancestor(name, len1, sha1, parent);  	} +	ret = peel_onion(name, len, sha1); +	if (!ret) +		return 0; +  	ret = get_sha1_basic(name, len, sha1);  	if (!ret)  		return 0;  | 
