aboutsummaryrefslogtreecommitdiff
path: root/sha1_name.c
diff options
context:
space:
mode:
authorJunio C Hamano <junkio@cox.net>2005-10-13 18:57:40 -0700
committerJunio C Hamano <junkio@cox.net>2005-10-15 11:23:40 -0700
commit5385f52da80be0d01fda45be586eb186c2be83ee (patch)
tree631b2396995353f2682599d7f780180add76a3bf /sha1_name.c
parent1a7141ff28e217312da4b289127875905c6ec479 (diff)
downloadgit-5385f52da80be0d01fda45be586eb186c2be83ee.tar.gz
git-5385f52da80be0d01fda45be586eb186c2be83ee.tar.xz
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>
Diffstat (limited to 'sha1_name.c')
-rw-r--r--sha1_name.c83
1 files changed, 83 insertions, 0 deletions
diff --git a/sha1_name.c b/sha1_name.c
index 4e9a05233..75c688ecf 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;