Skip to content

Commit 5385f52

Browse files
author
Junio C Hamano
committed
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 <[email protected]>
1 parent 1a7141f commit 5385f52

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

sha1_name.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include "cache.h"
2+
#include "tag.h"
23
#include "commit.h"
4+
#include "tree.h"
5+
#include "blob.h"
36

47
static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
58
{
@@ -274,6 +277,82 @@ static int get_nth_ancestor(const char *name, int len,
274277
return 0;
275278
}
276279

280+
static int peel_onion(const char *name, int len, unsigned char *sha1)
281+
{
282+
unsigned char outer[20];
283+
const char *sp;
284+
const char *type_string = NULL;
285+
struct object *o;
286+
287+
/*
288+
* "ref^{type}" dereferences ref repeatedly until you cannot
289+
* dereference anymore, or you get an object of given type,
290+
* whichever comes first. "ref^{}" means just dereference
291+
* tags until you get a non-tag. "ref^0" is a shorthand for
292+
* "ref^{commit}". "commit^{tree}" could be used to find the
293+
* top-level tree of the given commit.
294+
*/
295+
if (len < 4 || name[len-1] != '}')
296+
return -1;
297+
298+
for (sp = name + len - 1; name <= sp; sp--) {
299+
int ch = *sp;
300+
if (ch == '{' && name < sp && sp[-1] == '^')
301+
break;
302+
}
303+
if (sp <= name)
304+
return -1;
305+
306+
sp++; /* beginning of type name, or closing brace for empty */
307+
if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
308+
type_string = commit_type;
309+
else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
310+
type_string = tree_type;
311+
else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
312+
type_string = blob_type;
313+
else if (sp[0] == '}')
314+
type_string = NULL;
315+
else
316+
return -1;
317+
318+
if (get_sha1_1(name, sp - name - 2, outer))
319+
return -1;
320+
321+
o = parse_object(outer);
322+
if (!o)
323+
return -1;
324+
if (!type_string) {
325+
o = deref_tag(o);
326+
memcpy(sha1, o->sha1, 20);
327+
}
328+
else {
329+
/* At this point, the syntax look correct, so
330+
* if we do not get the needed object, we should
331+
* barf.
332+
*/
333+
334+
while (1) {
335+
if (!o)
336+
return -1;
337+
if (o->type == type_string) {
338+
memcpy(sha1, o->sha1, 20);
339+
return 0;
340+
}
341+
if (o->type == tag_type)
342+
o = ((struct tag*) o)->tagged;
343+
else if (o->type == commit_type)
344+
o = &(((struct commit *) o)->tree->object);
345+
else
346+
return error("%.*s: expected %s type, but the object dereferences to %s type",
347+
len, name, type_string,
348+
o->type);
349+
if (!o->parsed)
350+
parse_object(o->sha1);
351+
}
352+
}
353+
return 0;
354+
}
355+
277356
static int get_sha1_1(const char *name, int len, unsigned char *sha1)
278357
{
279358
int parent, ret;
@@ -315,6 +394,10 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
315394
return get_nth_ancestor(name, len1, sha1, parent);
316395
}
317396

397+
ret = peel_onion(name, len, sha1);
398+
if (!ret)
399+
return 0;
400+
318401
ret = get_sha1_basic(name, len, sha1);
319402
if (!ret)
320403
return 0;

0 commit comments

Comments
 (0)