Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1652 lines (1450 sloc) 39.9 KB
#include "cache.h"
#include "config.h"
#include "tag.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
#include "tree-walk.h"
#include "refs.h"
#include "remote.h"
#include "dir.h"
#include "sha1-array.h"
#include "packfile.h"
static int get_oid_oneline(const char *, struct object_id *, struct commit_list *);
typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
struct disambiguate_state {
int len; /* length of prefix in hex chars */
char hex_pfx[GIT_MAX_HEXSZ + 1];
struct object_id bin_pfx;
disambiguate_hint_fn fn;
void *cb_data;
struct object_id candidate;
unsigned candidate_exists:1;
unsigned candidate_checked:1;
unsigned candidate_ok:1;
unsigned disambiguate_fn_used:1;
unsigned ambiguous:1;
unsigned always_call_fn:1;
};
static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
{
if (ds->always_call_fn) {
ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
return;
}
if (!ds->candidate_exists) {
/* this is the first candidate */
oidcpy(&ds->candidate, current);
ds->candidate_exists = 1;
return;
} else if (!oidcmp(&ds->candidate, current)) {
/* the same as what we already have seen */
return;
}
if (!ds->fn) {
/* cannot disambiguate between ds->candidate and current */
ds->ambiguous = 1;
return;
}
if (!ds->candidate_checked) {
ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data);
ds->disambiguate_fn_used = 1;
ds->candidate_checked = 1;
}
if (!ds->candidate_ok) {
/* discard the candidate; we know it does not satisfy fn */
oidcpy(&ds->candidate, current);
ds->candidate_checked = 0;
return;
}
/* if we reach this point, we know ds->candidate satisfies fn */
if (ds->fn(current, ds->cb_data)) {
/*
* if both current and candidate satisfy fn, we cannot
* disambiguate.
*/
ds->candidate_ok = 0;
ds->ambiguous = 1;
}
/* otherwise, current can be discarded and candidate is still good */
}
static int append_loose_object(const struct object_id *oid, const char *path,
void *data)
{
oid_array_append(data, oid);
return 0;
}
static int match_sha(unsigned, const unsigned char *, const unsigned char *);
static void find_short_object_filename(struct disambiguate_state *ds)
{
int subdir_nr = ds->bin_pfx.hash[0];
struct alternate_object_database *alt;
static struct alternate_object_database *fakeent;
if (!fakeent) {
/*
* Create a "fake" alternate object database that
* points to our own object database, to make it
* easier to get a temporary working space in
* alt->name/alt->base while iterating over the
* object databases including our own.
*/
fakeent = alloc_alt_odb(get_object_directory());
}
fakeent->next = alt_odb_list;
for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
int pos;
if (!alt->loose_objects_subdir_seen[subdir_nr]) {
struct strbuf *buf = alt_scratch_buf(alt);
for_each_file_in_obj_subdir(subdir_nr, buf,
append_loose_object,
NULL, NULL,
&alt->loose_objects_cache);
alt->loose_objects_subdir_seen[subdir_nr] = 1;
}
pos = oid_array_lookup(&alt->loose_objects_cache, &ds->bin_pfx);
if (pos < 0)
pos = -1 - pos;
while (!ds->ambiguous && pos < alt->loose_objects_cache.nr) {
const struct object_id *oid;
oid = alt->loose_objects_cache.oid + pos;
if (!match_sha(ds->len, ds->bin_pfx.hash, oid->hash))
break;
update_candidates(ds, oid);
pos++;
}
}
}
static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
{
do {
if (*a != *b)
return 0;
a++;
b++;
len -= 2;
} while (len > 1);
if (len)
if ((*a ^ *b) & 0xf0)
return 0;
return 1;
}
static void unique_in_pack(struct packed_git *p,
struct disambiguate_state *ds)
{
uint32_t num, last, i, first = 0;
const struct object_id *current = NULL;
open_pack_index(p);
num = p->num_objects;
last = num;
while (first < last) {
uint32_t mid = (first + last) / 2;
const unsigned char *current;
int cmp;
current = nth_packed_object_sha1(p, mid);
cmp = hashcmp(ds->bin_pfx.hash, current);
if (!cmp) {
first = mid;
break;
}
if (cmp > 0) {
first = mid+1;
continue;
}
last = mid;
}
/*
* At this point, "first" is the location of the lowest object
* with an object name that could match "bin_pfx". See if we have
* 0, 1 or more objects that actually match(es).
*/
for (i = first; i < num && !ds->ambiguous; i++) {
struct object_id oid;
current = nth_packed_object_oid(&oid, p, i);
if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash))
break;
update_candidates(ds, current);
}
}
static void find_short_packed_object(struct disambiguate_state *ds)
{
struct packed_git *p;
prepare_packed_git();
for (p = packed_git; p && !ds->ambiguous; p = p->next)
unique_in_pack(p, ds);
}
#define SHORT_NAME_NOT_FOUND (-1)
#define SHORT_NAME_AMBIGUOUS (-2)
static int finish_object_disambiguation(struct disambiguate_state *ds,
struct object_id *oid)
{
if (ds->ambiguous)
return SHORT_NAME_AMBIGUOUS;
if (!ds->candidate_exists)
return SHORT_NAME_NOT_FOUND;
if (!ds->candidate_checked)
/*
* If this is the only candidate, there is no point
* calling the disambiguation hint callback.
*
* On the other hand, if the current candidate
* replaced an earlier candidate that did _not_ pass
* the disambiguation hint callback, then we do have
* more than one objects that match the short name
* given, so we should make sure this one matches;
* otherwise, if we discovered this one and the one
* that we previously discarded in the reverse order,
* we would end up showing different results in the
* same repository!
*/
ds->candidate_ok = (!ds->disambiguate_fn_used ||
ds->fn(&ds->candidate, ds->cb_data));
if (!ds->candidate_ok)
return SHORT_NAME_AMBIGUOUS;
oidcpy(oid, &ds->candidate);
return 0;
}
static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
{
int kind = sha1_object_info(oid->hash, NULL);
return kind == OBJ_COMMIT;
}
static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused)
{
struct object *obj;
int kind;
kind = sha1_object_info(oid->hash, NULL);
if (kind == OBJ_COMMIT)
return 1;
if (kind != OBJ_TAG)
return 0;
/* We need to do this the hard way... */
obj = deref_tag(parse_object(oid), NULL, 0);
if (obj && obj->type == OBJ_COMMIT)
return 1;
return 0;
}
static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
{
int kind = sha1_object_info(oid->hash, NULL);
return kind == OBJ_TREE;
}
static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused)
{
struct object *obj;
int kind;
kind = sha1_object_info(oid->hash, NULL);
if (kind == OBJ_TREE || kind == OBJ_COMMIT)
return 1;
if (kind != OBJ_TAG)
return 0;
/* We need to do this the hard way... */
obj = deref_tag(parse_object(oid), NULL, 0);
if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
return 1;
return 0;
}
static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
{
int kind = sha1_object_info(oid->hash, NULL);
return kind == OBJ_BLOB;
}
static disambiguate_hint_fn default_disambiguate_hint;
int set_disambiguate_hint_config(const char *var, const char *value)
{
static const struct {
const char *name;
disambiguate_hint_fn fn;
} hints[] = {
{ "none", NULL },
{ "commit", disambiguate_commit_only },
{ "committish", disambiguate_committish_only },
{ "tree", disambiguate_tree_only },
{ "treeish", disambiguate_treeish_only },
{ "blob", disambiguate_blob_only }
};
int i;
if (!value)
return config_error_nonbool(var);
for (i = 0; i < ARRAY_SIZE(hints); i++) {
if (!strcasecmp(value, hints[i].name)) {
default_disambiguate_hint = hints[i].fn;
return 0;
}
}
return error("unknown hint type for '%s': %s", var, value);
}
static int init_object_disambiguation(const char *name, int len,
struct disambiguate_state *ds)
{
int i;
if (len < MINIMUM_ABBREV || len > GIT_SHA1_HEXSZ)
return -1;
memset(ds, 0, sizeof(*ds));
for (i = 0; i < len ;i++) {
unsigned char c = name[i];
unsigned char val;
if (c >= '0' && c <= '9')
val = c - '0';
else if (c >= 'a' && c <= 'f')
val = c - 'a' + 10;
else if (c >= 'A' && c <='F') {
val = c - 'A' + 10;
c -= 'A' - 'a';
}
else
return -1;
ds->hex_pfx[i] = c;
if (!(i & 1))
val <<= 4;
ds->bin_pfx.hash[i >> 1] |= val;
}
ds->len = len;
ds->hex_pfx[len] = '\0';
prepare_alt_odb();
return 0;
}
static int show_ambiguous_object(const struct object_id *oid, void *data)
{
const struct disambiguate_state *ds = data;
struct strbuf desc = STRBUF_INIT;
int type;
if (ds->fn && !ds->fn(oid, ds->cb_data))
return 0;
type = sha1_object_info(oid->hash, NULL);
if (type == OBJ_COMMIT) {
struct commit *commit = lookup_commit(oid);
if (commit) {
struct pretty_print_context pp = {0};
pp.date_mode.type = DATE_SHORT;
format_commit_message(commit, " %ad - %s", &desc, &pp);
}
} else if (type == OBJ_TAG) {
struct tag *tag = lookup_tag(oid);
if (!parse_tag(tag) && tag->tag)
strbuf_addf(&desc, " %s", tag->tag);
}
advise(" %s %s%s",
find_unique_abbrev(oid->hash, DEFAULT_ABBREV),
typename(type) ? typename(type) : "unknown type",
desc.buf);
strbuf_release(&desc);
return 0;
}
static int get_short_oid(const char *name, int len, struct object_id *oid,
unsigned flags)
{
int status;
struct disambiguate_state ds;
int quietly = !!(flags & GET_OID_QUIETLY);
if (init_object_disambiguation(name, len, &ds) < 0)
return -1;
if (HAS_MULTI_BITS(flags & GET_OID_DISAMBIGUATORS))
die("BUG: multiple get_short_oid disambiguator flags");
if (flags & GET_OID_COMMIT)
ds.fn = disambiguate_commit_only;
else if (flags & GET_OID_COMMITTISH)
ds.fn = disambiguate_committish_only;
else if (flags & GET_OID_TREE)
ds.fn = disambiguate_tree_only;
else if (flags & GET_OID_TREEISH)
ds.fn = disambiguate_treeish_only;
else if (flags & GET_OID_BLOB)
ds.fn = disambiguate_blob_only;
else
ds.fn = default_disambiguate_hint;
find_short_object_filename(&ds);
find_short_packed_object(&ds);
status = finish_object_disambiguation(&ds, oid);
if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
error(_("short SHA1 %s is ambiguous"), ds.hex_pfx);
/*
* We may still have ambiguity if we simply saw a series of
* candidates that did not satisfy our hint function. In
* that case, we still want to show them, so disable the hint
* function entirely.
*/
if (!ds.ambiguous)
ds.fn = NULL;
advise(_("The candidates are:"));
for_each_abbrev(ds.hex_pfx, show_ambiguous_object, &ds);
}
return status;
}
static int collect_ambiguous(const struct object_id *oid, void *data)
{
oid_array_append(data, oid);
return 0;
}
int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
{
struct oid_array collect = OID_ARRAY_INIT;
struct disambiguate_state ds;
int ret;
if (init_object_disambiguation(prefix, strlen(prefix), &ds) < 0)
return -1;
ds.always_call_fn = 1;
ds.fn = collect_ambiguous;
ds.cb_data = &collect;
find_short_object_filename(&ds);
find_short_packed_object(&ds);
ret = oid_array_for_each_unique(&collect, fn, cb_data);
oid_array_clear(&collect);
return ret;
}
/*
* Return the slot of the most-significant bit set in "val". There are various
* ways to do this quickly with fls() or __builtin_clzl(), but speed is
* probably not a big deal here.
*/
static unsigned msb(unsigned long val)
{
unsigned r = 0;
while (val >>= 1)
r++;
return r;
}
int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
{
int status, exists;
if (len < 0) {
unsigned long count = approximate_object_count();
/*
* Add one because the MSB only tells us the highest bit set,
* not including the value of all the _other_ bits (so "15"
* is only one off of 2^4, but the MSB is the 3rd bit.
*/
len = msb(count) + 1;
/*
* We now know we have on the order of 2^len objects, which
* expects a collision at 2^(len/2). But we also care about hex
* chars, not bits, and there are 4 bits per hex. So all
* together we need to divide by 2 and round up.
*/
len = DIV_ROUND_UP(len, 2);
/*
* For very small repos, we stick with our regular fallback.
*/
if (len < FALLBACK_DEFAULT_ABBREV)
len = FALLBACK_DEFAULT_ABBREV;
}
sha1_to_hex_r(hex, sha1);
if (len == GIT_SHA1_HEXSZ || !len)
return GIT_SHA1_HEXSZ;
exists = has_sha1_file(sha1);
while (len < GIT_SHA1_HEXSZ) {
struct object_id oid_ret;
status = get_short_oid(hex, len, &oid_ret, GET_OID_QUIETLY);
if (exists
? !status
: status == SHORT_NAME_NOT_FOUND) {
hex[len] = 0;
return len;
}
len++;
}
return len;
}
const char *find_unique_abbrev(const unsigned char *sha1, int len)
{
static int bufno;
static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
char *hex = hexbuffer[bufno];
bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
find_unique_abbrev_r(hex, sha1, len);
return hex;
}
static int ambiguous_path(const char *path, int len)
{
int slash = 1;
int cnt;
for (cnt = 0; cnt < len; cnt++) {
switch (*path++) {
case '\0':
break;
case '/':
if (slash)
break;
slash = 1;
continue;
case '.':
continue;
default:
slash = 0;
continue;
}
break;
}
return slash;
}
static inline int at_mark(const char *string, int len,
const char **suffix, int nr)
{
int i;
for (i = 0; i < nr; i++) {
int suffix_len = strlen(suffix[i]);
if (suffix_len <= len
&& !strncasecmp(string, suffix[i], suffix_len))
return suffix_len;
}
return 0;
}
static inline int upstream_mark(const char *string, int len)
{
const char *suffix[] = { "@{upstream}", "@{u}" };
return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
}
static inline int push_mark(const char *string, int len)
{
const char *suffix[] = { "@{push}" };
return at_mark(string, len, suffix, ARRAY_SIZE(suffix));
}
static int get_oid_1(const char *name, int len, struct object_id *oid, unsigned lookup_flags);
static int interpret_nth_prior_checkout(const char *name, int namelen, struct strbuf *buf);
static int get_oid_basic(const char *str, int len, struct object_id *oid,
unsigned int flags)
{