2005-04-28 14:46:33 +00:00
|
|
|
#include "cache.h"
|
2006-01-07 09:33:54 +00:00
|
|
|
#include "tag.h"
|
2007-02-26 19:56:00 +00:00
|
|
|
#include "commit.h"
|
|
|
|
#include "tree.h"
|
|
|
|
#include "blob.h"
|
2005-04-28 14:46:33 +00:00
|
|
|
|
|
|
|
const char *tag_type = "tag";
|
|
|
|
|
2005-11-02 23:19:13 +00:00
|
|
|
struct object *deref_tag(struct object *o, const char *warn, int warnlen)
|
2005-08-05 07:47:56 +00:00
|
|
|
{
|
2006-07-12 03:45:31 +00:00
|
|
|
while (o && o->type == OBJ_TAG)
|
2008-02-18 07:31:55 +00:00
|
|
|
if (((struct tag *)o)->tagged)
|
|
|
|
o = parse_object(((struct tag *)o)->tagged->sha1);
|
|
|
|
else
|
|
|
|
o = NULL;
|
2005-11-02 23:19:13 +00:00
|
|
|
if (!o && warn) {
|
|
|
|
if (!warnlen)
|
|
|
|
warnlen = strlen(warn);
|
|
|
|
error("missing object referenced by '%.*s'", warnlen, warn);
|
|
|
|
}
|
2005-08-05 07:47:56 +00:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
upload-pack: avoid parsing tag destinations
When upload-pack advertises refs, it dereferences any tags
it sees, and shows the resulting sha1 to the client. It does
this by calling deref_tag. That function must load and parse
each tag object to find the sha1 of the tagged object.
However, it also ends up parsing the tagged object itself,
which is not strictly necessary for upload-pack's use.
Each tag produces two object loads (assuming it is not a
recursive tag), when it could get away with only a single
one. Dropping the second load halves the effort we spend.
The downside is that we are no longer verifying the
resulting object by loading it. In particular:
1. We never cross-check the "type" field given in the tag
object with the type of the pointed-to object. If the
tag says it points to a tag but doesn't, then we will
keep peeling and realize the error. If the tag says it
points to a non-tag but actually points to a tag, we
will stop peeling and just advertise the pointed-to
tag.
2. If we are missing the pointed-to object, we will not
realize (because we never even look it up in the object
db).
However, both of these are errors in the object database,
and both will be detected if a client actually requests the
broken objects in question. So we are simply pushing the
verification away from the advertising stage, and down to
the actual fetching stage.
On my test repo with 120K refs, this drops the time to
advertise the refs from ~3.2s to ~2.0s.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-01-06 19:18:01 +00:00
|
|
|
struct object *deref_tag_noverify(struct object *o)
|
|
|
|
{
|
|
|
|
while (o && o->type == OBJ_TAG) {
|
|
|
|
o = parse_object(o->sha1);
|
|
|
|
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
|
|
|
|
o = ((struct tag *)o)->tagged;
|
|
|
|
else
|
|
|
|
o = NULL;
|
|
|
|
}
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2005-06-03 15:05:39 +00:00
|
|
|
struct tag *lookup_tag(const unsigned char *sha1)
|
2005-04-28 14:46:33 +00:00
|
|
|
{
|
2007-04-17 05:11:43 +00:00
|
|
|
struct object *obj = lookup_object(sha1);
|
|
|
|
if (!obj)
|
2014-07-13 06:41:55 +00:00
|
|
|
return create_object(sha1, alloc_tag_node());
|
add object_as_type helper for casting objects
When we call lookup_commit, lookup_tree, etc, the logic goes
something like:
1. Look for an existing object struct. If we don't have
one, allocate and return a new one.
2. Double check that any object we have is the expected
type (and complain and return NULL otherwise).
3. Convert an object with type OBJ_NONE (from a prior
call to lookup_unknown_object) to the expected type.
We can encapsulate steps 2 and 3 in a helper function which
checks whether we have the expected object type, converts
OBJ_NONE as appropriate, and returns the object.
Not only does this shorten the code, but it also provides
one central location for converting OBJ_NONE objects into
objects of other types. Future patches will use that to
enforce type-specific invariants.
Since this is a refactoring, we would want it to behave
exactly as the current code. It takes a little reasoning to
see that this is the case:
- for lookup_{commit,tree,etc} functions, we are just
pulling steps 2 and 3 into a function that does the same
thing.
- for the call in peel_object, we currently only do step 3
(but we want to consolidate it with the others, as
mentioned above). However, step 2 is a noop here, as the
surrounding conditional makes sure we have OBJ_NONE
(which we want to keep to avoid an extraneous call to
sha1_object_info).
- for the call in lookup_commit_reference_gently, we are
currently doing step 2 but not step 3. However, step 3
is a noop here. The object we got will have just come
from deref_tag, which must have figured out the type for
each object in order to know when to stop peeling.
Therefore the type will never be OBJ_NONE.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2014-07-13 06:42:03 +00:00
|
|
|
return object_as_type(obj, OBJ_TAG, 0);
|
2005-04-28 14:46:33 +00:00
|
|
|
}
|
|
|
|
|
2010-04-12 23:25:28 +00:00
|
|
|
static unsigned long parse_tag_date(const char *buf, const char *tail)
|
|
|
|
{
|
|
|
|
const char *dateptr;
|
|
|
|
|
|
|
|
while (buf < tail && *buf++ != '>')
|
|
|
|
/* nada */;
|
|
|
|
if (buf >= tail)
|
|
|
|
return 0;
|
|
|
|
dateptr = buf;
|
|
|
|
while (buf < tail && *buf++ != '\n')
|
|
|
|
/* nada */;
|
|
|
|
if (buf >= tail)
|
|
|
|
return 0;
|
|
|
|
/* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
|
|
|
|
return strtoul(dateptr, NULL, 10);
|
|
|
|
}
|
|
|
|
|
2011-02-05 10:52:20 +00:00
|
|
|
int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
|
2005-04-28 14:46:33 +00:00
|
|
|
{
|
2007-02-26 19:56:00 +00:00
|
|
|
unsigned char sha1[20];
|
2005-06-22 00:35:10 +00:00
|
|
|
char type[20];
|
2010-04-12 23:25:27 +00:00
|
|
|
const char *bufptr = data;
|
|
|
|
const char *tail = bufptr + size;
|
|
|
|
const char *nl;
|
2005-04-30 16:51:03 +00:00
|
|
|
|
2010-04-12 23:25:25 +00:00
|
|
|
if (item->object.parsed)
|
|
|
|
return 0;
|
|
|
|
item->object.parsed = 1;
|
2005-04-28 14:46:33 +00:00
|
|
|
|
|
|
|
if (size < 64)
|
2005-05-06 17:48:34 +00:00
|
|
|
return -1;
|
2010-04-12 23:25:27 +00:00
|
|
|
if (memcmp("object ", bufptr, 7) || get_sha1_hex(bufptr + 7, sha1) || bufptr[47] != '\n')
|
2005-05-06 17:48:34 +00:00
|
|
|
return -1;
|
2010-04-12 23:25:27 +00:00
|
|
|
bufptr += 48; /* "object " + sha1 + "\n" */
|
2005-04-28 14:46:33 +00:00
|
|
|
|
2013-11-30 20:55:40 +00:00
|
|
|
if (!starts_with(bufptr, "type "))
|
2005-05-06 17:48:34 +00:00
|
|
|
return -1;
|
2010-04-12 23:25:27 +00:00
|
|
|
bufptr += 5;
|
|
|
|
nl = memchr(bufptr, '\n', tail - bufptr);
|
|
|
|
if (!nl || sizeof(type) <= (nl - bufptr))
|
2005-05-06 17:48:34 +00:00
|
|
|
return -1;
|
2010-04-12 23:25:27 +00:00
|
|
|
strncpy(type, bufptr, nl - bufptr);
|
|
|
|
type[nl - bufptr] = '\0';
|
|
|
|
bufptr = nl + 1;
|
2005-04-28 14:46:33 +00:00
|
|
|
|
2007-02-26 19:56:00 +00:00
|
|
|
if (!strcmp(type, blob_type)) {
|
|
|
|
item->tagged = &lookup_blob(sha1)->object;
|
|
|
|
} else if (!strcmp(type, tree_type)) {
|
|
|
|
item->tagged = &lookup_tree(sha1)->object;
|
|
|
|
} else if (!strcmp(type, commit_type)) {
|
|
|
|
item->tagged = &lookup_commit(sha1)->object;
|
|
|
|
} else if (!strcmp(type, tag_type)) {
|
|
|
|
item->tagged = &lookup_tag(sha1)->object;
|
|
|
|
} else {
|
|
|
|
error("Unknown type %s", type);
|
|
|
|
item->tagged = NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-30 20:55:40 +00:00
|
|
|
if (bufptr + 4 < tail && starts_with(bufptr, "tag "))
|
2011-02-14 13:02:51 +00:00
|
|
|
; /* good */
|
|
|
|
else
|
2010-04-12 23:25:27 +00:00
|
|
|
return -1;
|
|
|
|
bufptr += 4;
|
|
|
|
nl = memchr(bufptr, '\n', tail - bufptr);
|
|
|
|
if (!nl)
|
|
|
|
return -1;
|
|
|
|
item->tag = xmemdupz(bufptr, nl - bufptr);
|
|
|
|
bufptr = nl + 1;
|
|
|
|
|
2013-11-30 20:55:40 +00:00
|
|
|
if (bufptr + 7 < tail && starts_with(bufptr, "tagger "))
|
2010-04-12 23:25:28 +00:00
|
|
|
item->date = parse_tag_date(bufptr, tail);
|
|
|
|
else
|
|
|
|
item->date = 0;
|
|
|
|
|
2005-04-28 14:46:33 +00:00
|
|
|
return 0;
|
2005-05-06 17:48:34 +00:00
|
|
|
}
|
2005-05-04 17:44:15 +00:00
|
|
|
|
2005-05-06 17:48:34 +00:00
|
|
|
int parse_tag(struct tag *item)
|
|
|
|
{
|
2007-02-26 19:55:59 +00:00
|
|
|
enum object_type type;
|
2005-05-06 17:48:34 +00:00
|
|
|
void *data;
|
|
|
|
unsigned long size;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (item->object.parsed)
|
|
|
|
return 0;
|
2007-02-26 19:55:59 +00:00
|
|
|
data = read_sha1_file(item->object.sha1, &type, &size);
|
2005-05-06 17:48:34 +00:00
|
|
|
if (!data)
|
|
|
|
return error("Could not read %s",
|
|
|
|
sha1_to_hex(item->object.sha1));
|
2007-02-26 19:55:59 +00:00
|
|
|
if (type != OBJ_TAG) {
|
2005-05-06 17:48:34 +00:00
|
|
|
free(data);
|
|
|
|
return error("Object %s not a tag",
|
|
|
|
sha1_to_hex(item->object.sha1));
|
|
|
|
}
|
|
|
|
ret = parse_tag_buffer(item, data, size);
|
2005-05-04 17:44:15 +00:00
|
|
|
free(data);
|
2005-05-06 17:48:34 +00:00
|
|
|
return ret;
|
2005-04-28 14:46:33 +00:00
|
|
|
}
|