2005-08-04 09:15:49 +04:00
|
|
|
#include "cache.h"
|
2005-10-14 05:57:40 +04:00
|
|
|
#include "tag.h"
|
2005-08-04 09:15:49 +04:00
|
|
|
#include "commit.h"
|
2005-10-14 05:57:40 +04:00
|
|
|
#include "tree.h"
|
|
|
|
#include "blob.h"
|
2006-04-19 22:56:53 +04:00
|
|
|
#include "tree-walk.h"
|
2006-05-17 13:56:09 +04:00
|
|
|
#include "refs.h"
|
2009-09-10 19:25:57 +04:00
|
|
|
#include "remote.h"
|
2005-08-04 09:15:49 +04:00
|
|
|
|
2010-12-13 06:01:15 +03:00
|
|
|
static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
|
|
|
|
|
2012-06-21 09:07:36 +04:00
|
|
|
typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
|
|
|
|
|
|
|
|
struct disambiguate_state {
|
|
|
|
disambiguate_hint_fn fn;
|
|
|
|
void *cb_data;
|
|
|
|
unsigned char candidate[20];
|
|
|
|
unsigned candidate_exists:1;
|
|
|
|
unsigned candidate_checked:1;
|
|
|
|
unsigned candidate_ok:1;
|
|
|
|
unsigned disambiguate_fn_used:1;
|
|
|
|
unsigned ambiguous:1;
|
2012-07-04 01:21:59 +04:00
|
|
|
unsigned always_call_fn:1;
|
2012-06-21 09:07:36 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
|
|
|
|
{
|
2012-07-04 01:21:59 +04:00
|
|
|
if (ds->always_call_fn) {
|
|
|
|
ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
|
|
|
|
return;
|
|
|
|
}
|
2012-06-21 09:07:36 +04:00
|
|
|
if (!ds->candidate_exists) {
|
|
|
|
/* this is the first candidate */
|
|
|
|
hashcpy(ds->candidate, current);
|
|
|
|
ds->candidate_exists = 1;
|
|
|
|
return;
|
|
|
|
} else if (!hashcmp(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) {
|
2013-07-23 01:02:23 +04:00
|
|
|
/* discard the candidate; we know it does not satisfy fn */
|
2012-06-21 09:07:36 +04:00
|
|
|
hashcpy(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 void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
|
2005-08-04 09:15:49 +04:00
|
|
|
{
|
2005-10-03 08:40:51 +04:00
|
|
|
struct alternate_object_database *alt;
|
2005-08-04 09:15:49 +04:00
|
|
|
char hex[40];
|
2005-10-03 08:40:51 +04:00
|
|
|
static struct alternate_object_database *fakeent;
|
|
|
|
|
|
|
|
if (!fakeent) {
|
2012-06-18 22:41:03 +04:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-10-03 08:40:51 +04:00
|
|
|
const char *objdir = get_object_directory();
|
|
|
|
int objdir_len = strlen(objdir);
|
|
|
|
int entlen = objdir_len + 43;
|
|
|
|
fakeent = xmalloc(sizeof(*fakeent) + entlen);
|
|
|
|
memcpy(fakeent->base, objdir, objdir_len);
|
|
|
|
fakeent->name = fakeent->base + objdir_len + 1;
|
|
|
|
fakeent->name[-1] = '/';
|
|
|
|
}
|
|
|
|
fakeent->next = alt_odb_list;
|
2005-08-04 09:15:49 +04:00
|
|
|
|
2012-06-21 09:35:43 +04:00
|
|
|
sprintf(hex, "%.2s", hex_pfx);
|
2012-06-21 09:07:36 +04:00
|
|
|
for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
|
2005-08-04 09:15:49 +04:00
|
|
|
struct dirent *de;
|
2005-10-03 08:40:51 +04:00
|
|
|
DIR *dir;
|
2012-06-21 09:35:43 +04:00
|
|
|
sprintf(alt->name, "%.2s/", hex_pfx);
|
2005-10-03 08:40:51 +04:00
|
|
|
dir = opendir(alt->base);
|
|
|
|
if (!dir)
|
|
|
|
continue;
|
2012-06-21 09:07:36 +04:00
|
|
|
|
|
|
|
while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
|
|
|
|
unsigned char sha1[20];
|
|
|
|
|
2005-08-04 09:15:49 +04:00
|
|
|
if (strlen(de->d_name) != 38)
|
|
|
|
continue;
|
2012-06-21 09:35:43 +04:00
|
|
|
if (memcmp(de->d_name, hex_pfx + 2, len - 2))
|
2005-08-04 09:15:49 +04:00
|
|
|
continue;
|
2012-06-21 09:07:36 +04:00
|
|
|
memcpy(hex + 2, de->d_name, 38);
|
|
|
|
if (!get_sha1_hex(hex, sha1))
|
|
|
|
update_candidates(ds, sha1);
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-06-21 09:07:36 +04:00
|
|
|
static void unique_in_pack(int len,
|
2012-06-21 09:35:43 +04:00
|
|
|
const unsigned char *bin_pfx,
|
2012-06-21 09:07:36 +04:00
|
|
|
struct packed_git *p,
|
|
|
|
struct disambiguate_state *ds)
|
2005-08-04 09:15:49 +04:00
|
|
|
{
|
2012-06-19 00:10:38 +04:00
|
|
|
uint32_t num, last, i, first = 0;
|
|
|
|
const unsigned char *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);
|
2012-06-21 09:35:43 +04:00
|
|
|
cmp = hashcmp(bin_pfx, current);
|
2012-06-19 00:10:38 +04:00
|
|
|
if (!cmp) {
|
|
|
|
first = mid;
|
|
|
|
break;
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
2012-06-19 00:10:38 +04:00
|
|
|
if (cmp > 0) {
|
|
|
|
first = mid+1;
|
|
|
|
continue;
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
2012-06-19 00:10:38 +04:00
|
|
|
last = mid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point, "first" is the location of the lowest object
|
2012-06-21 09:35:43 +04:00
|
|
|
* with an object name that could match "bin_pfx". See if we have
|
2012-06-19 00:10:38 +04:00
|
|
|
* 0, 1 or more objects that actually match(es).
|
|
|
|
*/
|
2012-06-21 09:07:36 +04:00
|
|
|
for (i = first; i < num && !ds->ambiguous; i++) {
|
|
|
|
current = nth_packed_object_sha1(p, i);
|
2012-06-21 09:35:43 +04:00
|
|
|
if (!match_sha(len, bin_pfx, current))
|
2012-06-19 00:10:38 +04:00
|
|
|
break;
|
2012-06-21 09:07:36 +04:00
|
|
|
update_candidates(ds, current);
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
2012-06-19 00:10:38 +04:00
|
|
|
}
|
|
|
|
|
2012-06-21 09:07:36 +04:00
|
|
|
static void find_short_packed_object(int len, const unsigned char *bin_pfx,
|
|
|
|
struct disambiguate_state *ds)
|
2005-08-04 09:15:49 +04:00
|
|
|
{
|
|
|
|
struct packed_git *p;
|
|
|
|
|
|
|
|
prepare_packed_git();
|
2012-06-21 09:07:36 +04:00
|
|
|
for (p = packed_git; p && !ds->ambiguous; p = p->next)
|
|
|
|
unique_in_pack(len, bin_pfx, p, ds);
|
2005-10-03 08:40:51 +04:00
|
|
|
}
|
|
|
|
|
2005-10-12 02:22:48 +04:00
|
|
|
#define SHORT_NAME_NOT_FOUND (-1)
|
|
|
|
#define SHORT_NAME_AMBIGUOUS (-2)
|
|
|
|
|
2012-06-21 09:07:36 +04:00
|
|
|
static int finish_object_disambiguation(struct disambiguate_state *ds,
|
|
|
|
unsigned char *sha1)
|
2005-10-03 08:40:51 +04:00
|
|
|
{
|
2012-06-21 09:07:36 +04:00
|
|
|
if (ds->ambiguous)
|
|
|
|
return SHORT_NAME_AMBIGUOUS;
|
2005-10-03 08:40:51 +04:00
|
|
|
|
2012-06-21 09:07:36 +04:00
|
|
|
if (!ds->candidate_exists)
|
2005-10-12 02:22:48 +04:00
|
|
|
return SHORT_NAME_NOT_FOUND;
|
2012-06-21 09:07:36 +04:00
|
|
|
|
|
|
|
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)
|
2005-10-12 02:22:48 +04:00
|
|
|
return SHORT_NAME_AMBIGUOUS;
|
2012-06-21 09:07:36 +04:00
|
|
|
|
|
|
|
hashcpy(sha1, ds->candidate);
|
2005-08-04 09:15:49 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-21 10:03:09 +04:00
|
|
|
static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
|
|
|
|
{
|
|
|
|
int kind = sha1_object_info(sha1, NULL);
|
|
|
|
return kind == OBJ_COMMIT;
|
|
|
|
}
|
|
|
|
|
2012-07-02 21:00:40 +04:00
|
|
|
static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
|
|
|
|
{
|
|
|
|
struct object *obj;
|
|
|
|
int kind;
|
|
|
|
|
|
|
|
kind = sha1_object_info(sha1, NULL);
|
|
|
|
if (kind == OBJ_COMMIT)
|
|
|
|
return 1;
|
|
|
|
if (kind != OBJ_TAG)
|
2005-10-03 08:40:51 +04:00
|
|
|
return 0;
|
2012-07-02 21:00:40 +04:00
|
|
|
|
|
|
|
/* We need to do this the hard way... */
|
get_short_sha1(): correctly disambiguate type-limited abbreviation
One test in t1512 that expects a failure incorrectly passed. The
test prepares a commit whose object name begins with ten "0"s, and
also prepares a tag that points at the commit. The object name of
the tag also begins with ten "0"s. There is no other commit-ish
object in the repository whose name begins with such a prefix.
Ideally, in such a repository:
$ git rev-parse --verify 0000000000^{commit}
should yield that commit. If 0000000000 is taken as the commit
0000000000e4f, peeling it to a commmit yields that commit itself,
and if 0000000000 is taken as the tag 0000000000f8f, peeling it to a
commit also yields the same commit, so in that twisted sense, the
extended SHA-1 expression 0000000000^{commit} is unambigous. The
test that expects a failure is to check the above command.
The reason the test expects a failure is that we did not implement
such a "unification" of two candidate objects. What we did (or at
least, meant to) implement was to recognise that a commit-ish is
required to expand 0000000000, and notice that there are two succh
commit-ish, and diagnose the request as ambiguous.
However, there was a bug in the logic to check the candidate
objects. When the code saw 0000000000f8f (a tag) that shared the
shortened prefix (ten "0"s), it tried to make sure that the tag is a
commit-ish by looking at the tag object. Because it incorrectly
used lookup_object() when the tag has not been parsed, however, we
incorrectly declared that the tag is _not_ a commit-ish, leaving the
sole commit in the repository, 0000000000e4f, that has the required
prefix as "unique match", causing the test to pass when it shouldn't.
This fixes the logic to inspect the type of the object a tag refers
to, to make the test that is expected to fail correctly fail.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-02 08:54:45 +04:00
|
|
|
obj = deref_tag(parse_object(sha1), NULL, 0);
|
2012-07-02 21:00:40 +04:00
|
|
|
if (obj && obj->type == OBJ_COMMIT)
|
|
|
|
return 1;
|
2005-08-04 09:15:49 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-03 10:35:05 +04:00
|
|
|
static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
|
2005-08-04 09:15:49 +04:00
|
|
|
{
|
2012-07-03 10:35:05 +04:00
|
|
|
int kind = sha1_object_info(sha1, NULL);
|
|
|
|
return kind == OBJ_TREE;
|
|
|
|
}
|
2005-08-04 09:15:49 +04:00
|
|
|
|
2012-07-03 10:35:05 +04:00
|
|
|
static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
|
|
|
|
{
|
|
|
|
struct object *obj;
|
|
|
|
int kind;
|
|
|
|
|
|
|
|
kind = sha1_object_info(sha1, 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(lookup_object(sha1), NULL, 0);
|
|
|
|
if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
|
|
|
|
{
|
|
|
|
int kind = sha1_object_info(sha1, NULL);
|
|
|
|
return kind == OBJ_BLOB;
|
|
|
|
}
|
|
|
|
|
2012-07-04 01:21:59 +04:00
|
|
|
static int prepare_prefixes(const char *name, int len,
|
|
|
|
unsigned char *bin_pfx,
|
|
|
|
char *hex_pfx)
|
2005-08-04 09:15:49 +04:00
|
|
|
{
|
2012-07-04 01:21:59 +04:00
|
|
|
int i;
|
2005-08-04 09:15:49 +04:00
|
|
|
|
2012-06-21 09:35:43 +04:00
|
|
|
hashclr(bin_pfx);
|
|
|
|
memset(hex_pfx, 'x', 40);
|
2005-09-20 02:16:03 +04:00
|
|
|
for (i = 0; i < len ;i++) {
|
2005-08-04 09:15:49 +04:00
|
|
|
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;
|
2012-06-21 09:35:43 +04:00
|
|
|
hex_pfx[i] = c;
|
2005-08-04 09:15:49 +04:00
|
|
|
if (!(i & 1))
|
|
|
|
val <<= 4;
|
2012-06-21 09:35:43 +04:00
|
|
|
bin_pfx[i >> 1] |= val;
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
2012-07-04 01:21:59 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int get_short_sha1(const char *name, int len, unsigned char *sha1,
|
|
|
|
unsigned flags)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
char hex_pfx[40];
|
|
|
|
unsigned char bin_pfx[20];
|
|
|
|
struct disambiguate_state ds;
|
|
|
|
int quietly = !!(flags & GET_SHA1_QUIETLY);
|
|
|
|
|
|
|
|
if (len < MINIMUM_ABBREV || len > 40)
|
|
|
|
return -1;
|
|
|
|
if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
|
|
|
|
return -1;
|
2005-10-03 08:40:51 +04:00
|
|
|
|
2012-06-21 09:07:36 +04:00
|
|
|
prepare_alt_odb();
|
|
|
|
|
|
|
|
memset(&ds, 0, sizeof(ds));
|
2012-06-21 10:03:09 +04:00
|
|
|
if (flags & GET_SHA1_COMMIT)
|
|
|
|
ds.fn = disambiguate_commit_only;
|
2012-07-02 21:00:40 +04:00
|
|
|
else if (flags & GET_SHA1_COMMITTISH)
|
|
|
|
ds.fn = disambiguate_committish_only;
|
2012-07-03 10:35:05 +04:00
|
|
|
else if (flags & GET_SHA1_TREE)
|
|
|
|
ds.fn = disambiguate_tree_only;
|
|
|
|
else if (flags & GET_SHA1_TREEISH)
|
|
|
|
ds.fn = disambiguate_treeish_only;
|
|
|
|
else if (flags & GET_SHA1_BLOB)
|
|
|
|
ds.fn = disambiguate_blob_only;
|
2012-06-21 10:03:09 +04:00
|
|
|
|
2012-06-21 09:07:36 +04:00
|
|
|
find_short_object_filename(len, hex_pfx, &ds);
|
|
|
|
find_short_packed_object(len, bin_pfx, &ds);
|
|
|
|
status = finish_object_disambiguation(&ds, sha1);
|
2005-10-03 08:40:51 +04:00
|
|
|
|
2005-10-12 02:22:48 +04:00
|
|
|
if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
|
2012-06-21 09:35:43 +04:00
|
|
|
return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
|
2005-10-12 02:22:48 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-07-04 01:21:59 +04:00
|
|
|
|
|
|
|
int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
|
|
|
|
{
|
|
|
|
char hex_pfx[40];
|
|
|
|
unsigned char bin_pfx[20];
|
|
|
|
struct disambiguate_state ds;
|
|
|
|
int len = strlen(prefix);
|
|
|
|
|
|
|
|
if (len < MINIMUM_ABBREV || len > 40)
|
|
|
|
return -1;
|
|
|
|
if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
prepare_alt_odb();
|
|
|
|
|
|
|
|
memset(&ds, 0, sizeof(ds));
|
|
|
|
ds.always_call_fn = 1;
|
|
|
|
ds.cb_data = cb_data;
|
|
|
|
ds.fn = fn;
|
|
|
|
|
|
|
|
find_short_object_filename(len, hex_pfx, &ds);
|
|
|
|
find_short_packed_object(len, bin_pfx, &ds);
|
|
|
|
return ds.ambiguous;
|
|
|
|
}
|
|
|
|
|
2005-10-12 02:22:48 +04:00
|
|
|
const char *find_unique_abbrev(const unsigned char *sha1, int len)
|
|
|
|
{
|
2008-03-02 10:35:32 +03:00
|
|
|
int status, exists;
|
2005-10-12 02:22:48 +04:00
|
|
|
static char hex[41];
|
2005-12-14 04:21:41 +03:00
|
|
|
|
2008-03-02 10:35:32 +03:00
|
|
|
exists = has_sha1_file(sha1);
|
2005-10-12 02:22:48 +04:00
|
|
|
memcpy(hex, sha1_to_hex(sha1), 40);
|
2006-08-10 00:17:04 +04:00
|
|
|
if (len == 40 || !len)
|
2005-12-14 04:21:41 +03:00
|
|
|
return hex;
|
2005-10-12 02:22:48 +04:00
|
|
|
while (len < 40) {
|
|
|
|
unsigned char sha1_ret[20];
|
2012-06-18 22:32:03 +04:00
|
|
|
status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
|
2008-03-02 10:35:32 +03:00
|
|
|
if (exists
|
|
|
|
? !status
|
|
|
|
: status == SHORT_NAME_NOT_FOUND) {
|
2011-03-11 09:41:14 +03:00
|
|
|
hex[len] = 0;
|
2005-10-12 02:22:48 +04:00
|
|
|
return hex;
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
}
|
2008-03-02 10:35:32 +03:00
|
|
|
return hex;
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
|
|
|
|
2005-12-15 23:54:00 +03:00
|
|
|
static int ambiguous_path(const char *path, int len)
|
Be more careful about reference parsing
This does two things:
- we don't allow "." and ".." as components of a refname. Thus get_sha1()
will not accept "./refname" as being the same as "refname" any more.
- git-rev-parse stops doing revision translation after seeing a pathname,
to match the brhaviour of all the tools (once we see a pathname,
everything else will also be parsed as a pathname).
Basically, if you did
git log *
and "gitk" was somewhere in the "*", we don't want to replace the filename
"gitk" with the SHA1 of the branch with the same name.
Of course, if there is any change of ambiguity, you should always use "--"
to make it explicit what are filenames and what are revisions, but this
makes the normal cases sane. The refname rule also means that instead of
the "--", you can do the same thing we're used to doing with filenames
that start with a slash: use "./filename" instead, and now it's a
filename, not an option (and not a revision).
So "git log ./*.c" is now actually a perfectly valid thing to do, even if
the first C-file might have the same name as a branch.
Trivial test:
git-rev-parse gitk ./gitk gitk
should output something like
9843c3074dfbf57117565f6b7c93e3e6812857ee
./gitk
gitk
where the "./gitk" isn't seen as a revision, and the second "gitk" is a
filename simply because we've seen filenames already, and thus stopped
doing revision parsing.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-10-28 23:41:49 +04:00
|
|
|
{
|
|
|
|
int slash = 1;
|
2005-12-15 23:54:00 +03:00
|
|
|
int cnt;
|
Be more careful about reference parsing
This does two things:
- we don't allow "." and ".." as components of a refname. Thus get_sha1()
will not accept "./refname" as being the same as "refname" any more.
- git-rev-parse stops doing revision translation after seeing a pathname,
to match the brhaviour of all the tools (once we see a pathname,
everything else will also be parsed as a pathname).
Basically, if you did
git log *
and "gitk" was somewhere in the "*", we don't want to replace the filename
"gitk" with the SHA1 of the branch with the same name.
Of course, if there is any change of ambiguity, you should always use "--"
to make it explicit what are filenames and what are revisions, but this
makes the normal cases sane. The refname rule also means that instead of
the "--", you can do the same thing we're used to doing with filenames
that start with a slash: use "./filename" instead, and now it's a
filename, not an option (and not a revision).
So "git log ./*.c" is now actually a perfectly valid thing to do, even if
the first C-file might have the same name as a branch.
Trivial test:
git-rev-parse gitk ./gitk gitk
should output something like
9843c3074dfbf57117565f6b7c93e3e6812857ee
./gitk
gitk
where the "./gitk" isn't seen as a revision, and the second "gitk" is a
filename simply because we've seen filenames already, and thus stopped
doing revision parsing.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-10-28 23:41:49 +04:00
|
|
|
|
2005-12-15 23:54:00 +03:00
|
|
|
for (cnt = 0; cnt < len; cnt++) {
|
Be more careful about reference parsing
This does two things:
- we don't allow "." and ".." as components of a refname. Thus get_sha1()
will not accept "./refname" as being the same as "refname" any more.
- git-rev-parse stops doing revision translation after seeing a pathname,
to match the brhaviour of all the tools (once we see a pathname,
everything else will also be parsed as a pathname).
Basically, if you did
git log *
and "gitk" was somewhere in the "*", we don't want to replace the filename
"gitk" with the SHA1 of the branch with the same name.
Of course, if there is any change of ambiguity, you should always use "--"
to make it explicit what are filenames and what are revisions, but this
makes the normal cases sane. The refname rule also means that instead of
the "--", you can do the same thing we're used to doing with filenames
that start with a slash: use "./filename" instead, and now it's a
filename, not an option (and not a revision).
So "git log ./*.c" is now actually a perfectly valid thing to do, even if
the first C-file might have the same name as a branch.
Trivial test:
git-rev-parse gitk ./gitk gitk
should output something like
9843c3074dfbf57117565f6b7c93e3e6812857ee
./gitk
gitk
where the "./gitk" isn't seen as a revision, and the second "gitk" is a
filename simply because we've seen filenames already, and thus stopped
doing revision parsing.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-10-28 23:41:49 +04:00
|
|
|
switch (*path++) {
|
|
|
|
case '\0':
|
|
|
|
break;
|
|
|
|
case '/':
|
|
|
|
if (slash)
|
|
|
|
break;
|
|
|
|
slash = 1;
|
|
|
|
continue;
|
|
|
|
case '.':
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
slash = 0;
|
|
|
|
continue;
|
|
|
|
}
|
2005-12-17 11:00:50 +03:00
|
|
|
break;
|
Be more careful about reference parsing
This does two things:
- we don't allow "." and ".." as components of a refname. Thus get_sha1()
will not accept "./refname" as being the same as "refname" any more.
- git-rev-parse stops doing revision translation after seeing a pathname,
to match the brhaviour of all the tools (once we see a pathname,
everything else will also be parsed as a pathname).
Basically, if you did
git log *
and "gitk" was somewhere in the "*", we don't want to replace the filename
"gitk" with the SHA1 of the branch with the same name.
Of course, if there is any change of ambiguity, you should always use "--"
to make it explicit what are filenames and what are revisions, but this
makes the normal cases sane. The refname rule also means that instead of
the "--", you can do the same thing we're used to doing with filenames
that start with a slash: use "./filename" instead, and now it's a
filename, not an option (and not a revision).
So "git log ./*.c" is now actually a perfectly valid thing to do, even if
the first C-file might have the same name as a branch.
Trivial test:
git-rev-parse gitk ./gitk gitk
should output something like
9843c3074dfbf57117565f6b7c93e3e6812857ee
./gitk
gitk
where the "./gitk" isn't seen as a revision, and the second "gitk" is a
filename simply because we've seen filenames already, and thus stopped
doing revision parsing.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-10-28 23:41:49 +04:00
|
|
|
}
|
2005-12-15 23:54:00 +03:00
|
|
|
return slash;
|
Be more careful about reference parsing
This does two things:
- we don't allow "." and ".." as components of a refname. Thus get_sha1()
will not accept "./refname" as being the same as "refname" any more.
- git-rev-parse stops doing revision translation after seeing a pathname,
to match the brhaviour of all the tools (once we see a pathname,
everything else will also be parsed as a pathname).
Basically, if you did
git log *
and "gitk" was somewhere in the "*", we don't want to replace the filename
"gitk" with the SHA1 of the branch with the same name.
Of course, if there is any change of ambiguity, you should always use "--"
to make it explicit what are filenames and what are revisions, but this
makes the normal cases sane. The refname rule also means that instead of
the "--", you can do the same thing we're used to doing with filenames
that start with a slash: use "./filename" instead, and now it's a
filename, not an option (and not a revision).
So "git log ./*.c" is now actually a perfectly valid thing to do, even if
the first C-file might have the same name as a branch.
Trivial test:
git-rev-parse gitk ./gitk gitk
should output something like
9843c3074dfbf57117565f6b7c93e3e6812857ee
./gitk
gitk
where the "./gitk" isn't seen as a revision, and the second "gitk" is a
filename simply because we've seen filenames already, and thus stopped
doing revision parsing.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-10-28 23:41:49 +04:00
|
|
|
}
|
|
|
|
|
2010-01-20 10:17:11 +03:00
|
|
|
static inline int upstream_mark(const char *string, int len)
|
|
|
|
{
|
|
|
|
const char *suffix[] = { "@{upstream}", "@{u}" };
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(suffix); i++) {
|
|
|
|
int suffix_len = strlen(suffix[i]);
|
|
|
|
if (suffix_len <= len
|
|
|
|
&& !memcmp(string, suffix[i], suffix_len))
|
|
|
|
return suffix_len;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-02 20:46:50 +04:00
|
|
|
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
|
2013-05-08 01:55:10 +04:00
|
|
|
static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf);
|
2009-01-17 19:09:55 +03:00
|
|
|
|
2007-01-19 12:15:15 +03:00
|
|
|
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
|
|
|
|
{
|
2010-08-24 08:52:43 +04:00
|
|
|
static const char *warn_msg = "refname '%.*s' is ambiguous.";
|
2013-05-29 16:12:42 +04:00
|
|
|
static const char *object_name_msg = N_(
|
|
|
|
"Git normally never creates a ref that ends with 40 hex characters\n"
|
|
|
|
"because it will be ignored when you just specify 40-hex. These refs\n"
|
|
|
|
"may be created by mistake. For example,\n"
|
|
|
|
"\n"
|
|
|
|
" git checkout -b $br $(git rev-parse ...)\n"
|
|
|
|
"\n"
|
|
|
|
"where \"$br\" is somehow empty and a 40-hex ref is created. Please\n"
|
|
|
|
"examine these refs and maybe delete them. Turn this message off by\n"
|
2013-08-01 00:23:31 +04:00
|
|
|
"running \"git config advice.objectNameWarning false\"");
|
2013-05-29 16:12:42 +04:00
|
|
|
unsigned char tmp_sha1[20];
|
2006-09-12 07:17:35 +04:00
|
|
|
char *real_ref = NULL;
|
2006-10-06 10:16:15 +04:00
|
|
|
int refs_found = 0;
|
2013-05-08 01:55:10 +04:00
|
|
|
int at, reflog_len, nth_prior = 0;
|
2005-08-04 09:15:49 +04:00
|
|
|
|
2013-05-29 16:12:42 +04:00
|
|
|
if (len == 40 && !get_sha1_hex(str, sha1)) {
|
cat-file: disable object/refname ambiguity check for batch mode
A common use of "cat-file --batch-check" is to feed a list
of objects from "rev-list --objects" or a similar command.
In this instance, all of our input objects are 40-byte sha1
ids. However, cat-file has always allowed arbitrary revision
specifiers, and feeds the result to get_sha1().
Fortunately, get_sha1() recognizes a 40-byte sha1 before
doing any hard work trying to look up refs, meaning this
scenario should end up spending very little time converting
the input into an object sha1. However, since 798c35f
(get_sha1: warn about full or short object names that look
like refs, 2013-05-29), when we encounter this case, we
spend the extra effort to do a refname lookup anyway, just
to print a warning. This is further exacerbated by ca91993
(get_packed_ref_cache: reload packed-refs file when it
changes, 2013-06-20), which makes individual ref lookup more
expensive by requiring a stat() of the packed-refs file for
each missing ref.
With no patches, this is the time it takes to run:
$ git rev-list --objects --all >objects
$ time git cat-file --batch-check='%(objectname)' <objects
on the linux.git repository:
real 1m13.494s
user 0m25.924s
sys 0m47.532s
If we revert ca91993, the packed-refs up-to-date check, it
gets a little better:
real 0m54.697s
user 0m21.692s
sys 0m32.916s
but we are still spending quite a bit of time on ref lookup
(and we would not want to revert that patch, anyway, which
has correctness issues). If we revert 798c35f, disabling
the warning entirely, we get a much more reasonable time:
real 0m7.452s
user 0m6.836s
sys 0m0.608s
This patch does the moral equivalent of this final case (and
gets similar speedups). We introduce a global flag that
callers of get_sha1() can use to avoid paying the price for
the warning.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-12 10:20:05 +04:00
|
|
|
if (warn_on_object_refname_ambiguity) {
|
|
|
|
refs_found = dwim_ref(str, len, tmp_sha1, &real_ref);
|
|
|
|
if (refs_found > 0 && warn_ambiguous_refs) {
|
|
|
|
warning(warn_msg, len, str);
|
|
|
|
if (advice_object_name_warning)
|
|
|
|
fprintf(stderr, "%s\n", _(object_name_msg));
|
|
|
|
}
|
|
|
|
free(real_ref);
|
2013-05-29 16:12:42 +04:00
|
|
|
}
|
2005-08-04 09:15:49 +04:00
|
|
|
return 0;
|
2013-05-29 16:12:42 +04:00
|
|
|
}
|
2005-08-04 09:15:49 +04:00
|
|
|
|
2009-01-17 19:09:55 +03:00
|
|
|
/* basic@{time or number or -number} format to query ref-log */
|
2006-10-24 08:15:34 +04:00
|
|
|
reflog_len = at = 0;
|
2009-01-28 02:07:46 +03:00
|
|
|
if (len && str[len-1] == '}') {
|
2013-05-08 01:55:09 +04:00
|
|
|
for (at = len-4; at >= 0; at--) {
|
2006-10-06 10:16:15 +04:00
|
|
|
if (str[at] == '@' && str[at+1] == '{') {
|
2013-05-08 01:55:11 +04:00
|
|
|
if (str[at+2] == '-') {
|
|
|
|
if (at != 0)
|
|
|
|
/* @{-N} not at start */
|
|
|
|
return -1;
|
2013-05-08 01:55:10 +04:00
|
|
|
nth_prior = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2010-01-20 10:17:11 +03:00
|
|
|
if (!upstream_mark(str + at, len - at)) {
|
2009-09-10 19:25:57 +04:00
|
|
|
reflog_len = (len-1) - (at+2);
|
|
|
|
len = at;
|
|
|
|
}
|
2006-10-06 10:16:15 +04:00
|
|
|
break;
|
|
|
|
}
|
2006-05-17 13:56:09 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Be more careful about reference parsing
This does two things:
- we don't allow "." and ".." as components of a refname. Thus get_sha1()
will not accept "./refname" as being the same as "refname" any more.
- git-rev-parse stops doing revision translation after seeing a pathname,
to match the brhaviour of all the tools (once we see a pathname,
everything else will also be parsed as a pathname).
Basically, if you did
git log *
and "gitk" was somewhere in the "*", we don't want to replace the filename
"gitk" with the SHA1 of the branch with the same name.
Of course, if there is any change of ambiguity, you should always use "--"
to make it explicit what are filenames and what are revisions, but this
makes the normal cases sane. The refname rule also means that instead of
the "--", you can do the same thing we're used to doing with filenames
that start with a slash: use "./filename" instead, and now it's a
filename, not an option (and not a revision).
So "git log ./*.c" is now actually a perfectly valid thing to do, even if
the first C-file might have the same name as a branch.
Trivial test:
git-rev-parse gitk ./gitk gitk
should output something like
9843c3074dfbf57117565f6b7c93e3e6812857ee
./gitk
gitk
where the "./gitk" isn't seen as a revision, and the second "gitk" is a
filename simply because we've seen filenames already, and thus stopped
doing revision parsing.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-10-28 23:41:49 +04:00
|
|
|
/* Accept only unambiguous ref paths. */
|
2007-02-02 01:29:33 +03:00
|
|
|
if (len && ambiguous_path(str, len))
|
Be more careful about reference parsing
This does two things:
- we don't allow "." and ".." as components of a refname. Thus get_sha1()
will not accept "./refname" as being the same as "refname" any more.
- git-rev-parse stops doing revision translation after seeing a pathname,
to match the brhaviour of all the tools (once we see a pathname,
everything else will also be parsed as a pathname).
Basically, if you did
git log *
and "gitk" was somewhere in the "*", we don't want to replace the filename
"gitk" with the SHA1 of the branch with the same name.
Of course, if there is any change of ambiguity, you should always use "--"
to make it explicit what are filenames and what are revisions, but this
makes the normal cases sane. The refname rule also means that instead of
the "--", you can do the same thing we're used to doing with filenames
that start with a slash: use "./filename" instead, and now it's a
filename, not an option (and not a revision).
So "git log ./*.c" is now actually a perfectly valid thing to do, even if
the first C-file might have the same name as a branch.
Trivial test:
git-rev-parse gitk ./gitk gitk
should output something like
9843c3074dfbf57117565f6b7c93e3e6812857ee
./gitk
gitk
where the "./gitk" isn't seen as a revision, and the second "gitk" is a
filename simply because we've seen filenames already, and thus stopped
doing revision parsing.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2005-10-28 23:41:49 +04:00
|
|
|
return -1;
|
|
|
|
|
2013-05-08 01:55:10 +04:00
|
|
|
if (nth_prior) {
|
2009-01-17 19:09:55 +03:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2013-05-08 01:55:10 +04:00
|
|
|
int detached;
|
|
|
|
|
|
|
|
if (interpret_nth_prior_checkout(str, &buf) > 0) {
|
|
|
|
detached = (buf.len == 40 && !get_sha1_hex(buf.buf, sha1));
|
|
|
|
strbuf_release(&buf);
|
|
|
|
if (detached)
|
|
|
|
return 0;
|
2009-01-17 19:09:55 +03:00
|
|
|
}
|
2013-05-08 01:55:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!len && reflog_len)
|
2007-02-02 01:29:33 +03:00
|
|
|
/* allow "@{...}" to mean the current branch reflog */
|
|
|
|
refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
|
2013-05-08 01:55:10 +04:00
|
|
|
else if (reflog_len)
|
2007-02-04 05:49:16 +03:00
|
|
|
refs_found = dwim_log(str, len, sha1, &real_ref);
|
|
|
|
else
|
2007-02-02 01:29:33 +03:00
|
|
|
refs_found = dwim_ref(str, len, sha1, &real_ref);
|
2006-05-17 13:56:09 +04:00
|
|
|
|
|
|
|
if (!refs_found)
|
|
|
|
return -1;
|
|
|
|
|
2013-05-29 16:12:42 +04:00
|
|
|
if (warn_ambiguous_refs &&
|
|
|
|
(refs_found > 1 ||
|
|
|
|
!get_short_sha1(str, len, tmp_sha1, GET_SHA1_QUIETLY)))
|
2010-08-24 08:52:43 +04:00
|
|
|
warning(warn_msg, len, str);
|
2006-05-17 13:56:09 +04:00
|
|
|
|
2006-10-06 10:16:15 +04:00
|
|
|
if (reflog_len) {
|
|
|
|
int nth, i;
|
|
|
|
unsigned long at_time;
|
2007-01-19 12:19:05 +03:00
|
|
|
unsigned long co_time;
|
|
|
|
int co_tz, co_cnt;
|
|
|
|
|
2007-02-01 20:33:23 +03:00
|
|
|
/* Is it asking for N-th entry, or approxidate? */
|
2006-10-06 10:16:15 +04:00
|
|
|
for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
|
|
|
|
char ch = str[at+2+i];
|
|
|
|
if ('0' <= ch && ch <= '9')
|
|
|
|
nth = nth * 10 + ch - '0';
|
|
|
|
else
|
|
|
|
nth = -1;
|
|
|
|
}
|
2008-08-21 19:40:44 +04:00
|
|
|
if (100000000 <= nth) {
|
|
|
|
at_time = nth;
|
|
|
|
nth = -1;
|
|
|
|
} else if (0 <= nth)
|
2006-10-06 10:16:15 +04:00
|
|
|
at_time = 0;
|
2008-04-30 08:13:58 +04:00
|
|
|
else {
|
2010-01-26 22:58:00 +03:00
|
|
|
int errors = 0;
|
2008-04-30 08:13:58 +04:00
|
|
|
char *tmp = xstrndup(str + at + 2, reflog_len);
|
2010-01-26 22:58:00 +03:00
|
|
|
at_time = approxidate_careful(tmp, &errors);
|
2008-04-30 08:13:58 +04:00
|
|
|
free(tmp);
|
2010-01-27 21:53:09 +03:00
|
|
|
if (errors)
|
|
|
|
return -1;
|
2008-04-30 08:13:58 +04:00
|
|
|
}
|
2007-01-19 12:19:05 +03:00
|
|
|
if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
|
|
|
|
&co_time, &co_tz, &co_cnt)) {
|
2013-05-22 14:39:55 +04:00
|
|
|
if (!len) {
|
|
|
|
if (!prefixcmp(real_ref, "refs/heads/")) {
|
|
|
|
str = real_ref + 11;
|
|
|
|
len = strlen(real_ref + 11);
|
|
|
|
} else {
|
|
|
|
/* detached HEAD */
|
|
|
|
str = "HEAD";
|
|
|
|
len = 4;
|
|
|
|
}
|
|
|
|
}
|
2007-01-19 12:19:05 +03:00
|
|
|
if (at_time)
|
2010-08-24 08:52:43 +04:00
|
|
|
warning("Log for '%.*s' only goes "
|
|
|
|
"back to %s.", len, str,
|
2007-07-14 10:14:52 +04:00
|
|
|
show_date(co_time, co_tz, DATE_RFC2822));
|
2010-08-24 08:52:42 +04:00
|
|
|
else {
|
|
|
|
die("Log for '%.*s' only has %d entries.",
|
|
|
|
len, str, co_cnt);
|
|
|
|
}
|
2007-01-19 12:19:05 +03:00
|
|
|
}
|
2006-05-17 13:56:09 +04:00
|
|
|
}
|
|
|
|
|
2006-09-12 07:17:35 +04:00
|
|
|
free(real_ref);
|
2006-05-17 13:56:09 +04:00
|
|
|
return 0;
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int get_parent(const char *name, int len,
|
|
|
|
unsigned char *result, int idx)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
2012-07-02 21:00:40 +04:00
|
|
|
int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
|
2005-08-04 09:15:49 +04:00
|
|
|
struct commit *commit;
|
|
|
|
struct commit_list *p;
|
|
|
|
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
commit = lookup_commit_reference(sha1);
|
|
|
|
if (!commit)
|
|
|
|
return -1;
|
|
|
|
if (parse_commit(commit))
|
|
|
|
return -1;
|
|
|
|
if (!idx) {
|
2006-08-23 10:49:00 +04:00
|
|
|
hashcpy(result, commit->object.sha1);
|
2005-08-04 09:15:49 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
p = commit->parents;
|
|
|
|
while (p) {
|
|
|
|
if (!--idx) {
|
2006-08-23 10:49:00 +04:00
|
|
|
hashcpy(result, p->item->object.sha1);
|
2005-08-04 09:15:49 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-08-21 13:43:54 +04:00
|
|
|
static int get_nth_ancestor(const char *name, int len,
|
|
|
|
unsigned char *result, int generation)
|
|
|
|
{
|
|
|
|
unsigned char sha1[20];
|
2008-03-14 21:49:40 +03:00
|
|
|
struct commit *commit;
|
|
|
|
int ret;
|
|
|
|
|
2012-07-02 21:00:40 +04:00
|
|
|
ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
|
2005-08-21 13:43:54 +04:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
2008-03-14 21:49:40 +03:00
|
|
|
commit = lookup_commit_reference(sha1);
|
|
|
|
if (!commit)
|
|
|
|
return -1;
|
2005-08-21 13:43:54 +04:00
|
|
|
|
|
|
|
while (generation--) {
|
2008-03-14 21:49:40 +03:00
|
|
|
if (parse_commit(commit) || !commit->parents)
|
2005-08-21 13:43:54 +04:00
|
|
|
return -1;
|
2008-03-14 21:49:40 +03:00
|
|
|
commit = commit->parents->item;
|
2005-08-21 13:43:54 +04:00
|
|
|
}
|
2008-03-14 21:49:40 +03:00
|
|
|
hashcpy(result, commit->object.sha1);
|
2005-08-21 13:43:54 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-24 11:51:01 +03:00
|
|
|
struct object *peel_to_type(const char *name, int namelen,
|
|
|
|
struct object *o, enum object_type expected_type)
|
|
|
|
{
|
|
|
|
if (name && !namelen)
|
|
|
|
namelen = strlen(name);
|
|
|
|
while (1) {
|
|
|
|
if (!o || (!o->parsed && !parse_object(o->sha1)))
|
|
|
|
return NULL;
|
2013-04-01 02:24:12 +04:00
|
|
|
if (expected_type == OBJ_ANY || o->type == expected_type)
|
2007-12-24 11:51:01 +03:00
|
|
|
return o;
|
|
|
|
if (o->type == OBJ_TAG)
|
|
|
|
o = ((struct tag*) o)->tagged;
|
|
|
|
else if (o->type == OBJ_COMMIT)
|
|
|
|
o = &(((struct commit *) o)->tree->object);
|
|
|
|
else {
|
|
|
|
if (name)
|
|
|
|
error("%.*s: expected %s type, but the object "
|
|
|
|
"dereferences to %s type",
|
|
|
|
namelen, name, typename(expected_type),
|
|
|
|
typename(o->type));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-14 05:57:40 +04:00
|
|
|
static int peel_onion(const char *name, int len, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
unsigned char outer[20];
|
|
|
|
const char *sp;
|
Shrink "struct object" a bit
This shrinks "struct object" by a small amount, by getting rid of the
"struct type *" pointer and replacing it with a 3-bit bitfield instead.
In addition, we merge the bitfields and the "flags" field, which
incidentally should also remove a useless 4-byte padding from the object
when in 64-bit mode.
Now, our "struct object" is still too damn large, but it's now less
obviously bloated, and of the remaining fields, only the "util" (which is
not used by most things) is clearly something that should be eventually
discarded.
This shrinks the "git-rev-list --all" memory use by about 2.5% on the
kernel archive (and, perhaps more importantly, on the larger mozilla
archive). That may not sound like much, but I suspect it's more on a
64-bit platform.
There are other remaining inefficiencies (the parent lists, for example,
probably have horrible malloc overhead), but this was pretty obvious.
Most of the patch is just changing the comparison of the "type" pointer
from one of the constant string pointers to the appropriate new TYPE_xxx
small integer constant.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-06-15 03:45:13 +04:00
|
|
|
unsigned int expected_type = 0;
|
2012-07-02 21:00:40 +04:00
|
|
|
unsigned lookup_flags = 0;
|
2005-10-14 05:57:40 +04:00
|
|
|
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] == '}')
|
2006-07-12 07:45:31 +04:00
|
|
|
expected_type = OBJ_COMMIT;
|
2005-10-14 05:57:40 +04:00
|
|
|
else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
|
2006-07-12 07:45:31 +04:00
|
|
|
expected_type = OBJ_TREE;
|
2005-10-14 05:57:40 +04:00
|
|
|
else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
|
2006-07-12 07:45:31 +04:00
|
|
|
expected_type = OBJ_BLOB;
|
2013-04-01 02:24:12 +04:00
|
|
|
else if (!prefixcmp(sp, "object}"))
|
|
|
|
expected_type = OBJ_ANY;
|
2005-10-14 05:57:40 +04:00
|
|
|
else if (sp[0] == '}')
|
2006-07-12 07:45:31 +04:00
|
|
|
expected_type = OBJ_NONE;
|
2010-12-13 06:01:15 +03:00
|
|
|
else if (sp[0] == '/')
|
|
|
|
expected_type = OBJ_COMMIT;
|
2005-10-14 05:57:40 +04:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
|
2012-07-02 21:00:40 +04:00
|
|
|
if (expected_type == OBJ_COMMIT)
|
|
|
|
lookup_flags = GET_SHA1_COMMITTISH;
|
2013-04-01 02:19:52 +04:00
|
|
|
else if (expected_type == OBJ_TREE)
|
|
|
|
lookup_flags = GET_SHA1_TREEISH;
|
2012-07-02 21:00:40 +04:00
|
|
|
|
|
|
|
if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
|
2005-10-14 05:57:40 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
o = parse_object(outer);
|
|
|
|
if (!o)
|
|
|
|
return -1;
|
Shrink "struct object" a bit
This shrinks "struct object" by a small amount, by getting rid of the
"struct type *" pointer and replacing it with a 3-bit bitfield instead.
In addition, we merge the bitfields and the "flags" field, which
incidentally should also remove a useless 4-byte padding from the object
when in 64-bit mode.
Now, our "struct object" is still too damn large, but it's now less
obviously bloated, and of the remaining fields, only the "util" (which is
not used by most things) is clearly something that should be eventually
discarded.
This shrinks the "git-rev-list --all" memory use by about 2.5% on the
kernel archive (and, perhaps more importantly, on the larger mozilla
archive). That may not sound like much, but I suspect it's more on a
64-bit platform.
There are other remaining inefficiencies (the parent lists, for example,
probably have horrible malloc overhead), but this was pretty obvious.
Most of the patch is just changing the comparison of the "type" pointer
from one of the constant string pointers to the appropriate new TYPE_xxx
small integer constant.
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
2006-06-15 03:45:13 +04:00
|
|
|
if (!expected_type) {
|
2005-11-03 02:19:13 +03:00
|
|
|
o = deref_tag(o, name, sp - name - 2);
|
2005-10-20 09:48:16 +04:00
|
|
|
if (!o || (!o->parsed && !parse_object(o->sha1)))
|
|
|
|
return -1;
|
2006-08-23 10:49:00 +04:00
|
|
|
hashcpy(sha1, o->sha1);
|
2010-12-13 06:01:15 +03:00
|
|
|
return 0;
|
2005-10-14 05:57:40 +04:00
|
|
|
}
|
2010-12-13 06:01:15 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point, the syntax look correct, so
|
|
|
|
* if we do not get the needed object, we should
|
|
|
|
* barf.
|
|
|
|
*/
|
|
|
|
o = peel_to_type(name, len, o, expected_type);
|
|
|
|
if (!o)
|
2007-12-24 11:51:01 +03:00
|
|
|
return -1;
|
2010-12-13 06:01:15 +03:00
|
|
|
|
|
|
|
hashcpy(sha1, o->sha1);
|
|
|
|
if (sp[0] == '/') {
|
|
|
|
/* "$commit^{/foo}" */
|
|
|
|
char *prefix;
|
|
|
|
int ret;
|
|
|
|
struct commit_list *list = NULL;
|
|
|
|
|
2007-12-24 11:51:01 +03:00
|
|
|
/*
|
2010-12-15 12:02:54 +03:00
|
|
|
* $commit^{/}. Some regex implementation may reject.
|
|
|
|
* We don't need regex anyway. '' pattern always matches.
|
2005-10-14 05:57:40 +04:00
|
|
|
*/
|
2010-12-15 12:02:54 +03:00
|
|
|
if (sp[1] == '}')
|
2007-12-24 11:51:01 +03:00
|
|
|
return 0;
|
2010-12-15 12:02:54 +03:00
|
|
|
|
2010-12-13 06:01:15 +03:00
|
|
|
prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1));
|
|
|
|
commit_list_insert((struct commit *)o, &list);
|
|
|
|
ret = get_sha1_oneline(prefix, sha1, list);
|
|
|
|
free(prefix);
|
|
|
|
return ret;
|
2005-10-14 05:57:40 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-21 03:11:08 +04:00
|
|
|
static int get_describe_name(const char *name, int len, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
const char *cp;
|
2012-06-19 00:45:56 +04:00
|
|
|
unsigned flags = GET_SHA1_QUIETLY | GET_SHA1_COMMIT;
|
2006-09-21 03:11:08 +04:00
|
|
|
|
|
|
|
for (cp = name + len - 1; name + 2 <= cp; cp--) {
|
|
|
|
char ch = *cp;
|
|
|
|
if (hexval(ch) & ~0377) {
|
|
|
|
/* We must be looking at g in "SOMETHING-g"
|
|
|
|
* for it to be describe output.
|
|
|
|
*/
|
|
|
|
if (ch == 'g' && cp[-1] == '-') {
|
|
|
|
cp++;
|
|
|
|
len -= cp - name;
|
2012-06-19 00:45:56 +04:00
|
|
|
return get_short_sha1(cp, len, sha1, flags);
|
2006-09-21 03:11:08 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-07-02 20:46:50 +04:00
|
|
|
static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags)
|
2005-08-04 09:15:49 +04:00
|
|
|
{
|
2006-02-03 10:48:36 +03:00
|
|
|
int ret, has_suffix;
|
2005-08-21 13:43:54 +04:00
|
|
|
const char *cp;
|
2005-08-04 09:15:49 +04:00
|
|
|
|
2008-03-14 21:49:40 +03:00
|
|
|
/*
|
|
|
|
* "name~3" is "name^^^", "name~" is "name~1", and "name^" is "name^1".
|
2005-08-21 13:43:54 +04:00
|
|
|
*/
|
2006-02-03 10:48:36 +03:00
|
|
|
has_suffix = 0;
|
2005-08-21 13:43:54 +04:00
|
|
|
for (cp = name + len - 1; name <= cp; cp--) {
|
|
|
|
int ch = *cp;
|
|
|
|
if ('0' <= ch && ch <= '9')
|
|
|
|
continue;
|
2006-02-03 10:48:36 +03:00
|
|
|
if (ch == '~' || ch == '^')
|
|
|
|
has_suffix = ch;
|
2005-08-21 13:43:54 +04:00
|
|
|
break;
|
|
|
|
}
|
2006-02-03 10:48:36 +03:00
|
|
|
|
|
|
|
if (has_suffix) {
|
|
|
|
int num = 0;
|
2005-08-21 13:43:54 +04:00
|
|
|
int len1 = cp - name;
|
|
|
|
cp++;
|
|
|
|
while (cp < name + len)
|
2006-02-03 10:48:36 +03:00
|
|
|
num = num * 10 + *cp++ - '0';
|
2008-03-14 21:49:40 +03:00
|
|
|
if (!num && len1 == len - 1)
|
|
|
|
num = 1;
|
|
|
|
if (has_suffix == '^')
|
2006-02-03 10:48:36 +03:00
|
|
|
return get_parent(name, len1, sha1, num);
|
|
|
|
/* else if (has_suffix == '~') -- goes without saying */
|
|
|
|
return get_nth_ancestor(name, len1, sha1, num);
|
2005-08-21 13:43:54 +04:00
|
|
|
}
|
|
|
|
|
2005-10-14 05:57:40 +04:00
|
|
|
ret = peel_onion(name, len, sha1);
|
|
|
|
if (!ret)
|
|
|
|
return 0;
|
|
|
|
|
2005-08-04 09:15:49 +04:00
|
|
|
ret = get_sha1_basic(name, len, sha1);
|
|
|
|
if (!ret)
|
|
|
|
return 0;
|
2006-09-21 03:11:08 +04:00
|
|
|
|
|
|
|
/* It could be describe output that is "SOMETHING-gXXXX" */
|
|
|
|
ret = get_describe_name(name, len, sha1);
|
|
|
|
if (!ret)
|
|
|
|
return 0;
|
|
|
|
|
2012-07-02 21:00:40 +04:00
|
|
|
return get_short_sha1(name, len, sha1, lookup_flags);
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
|
|
|
|
2010-08-03 01:37:06 +04:00
|
|
|
/*
|
|
|
|
* This interprets names like ':/Initial revision of "git"' by searching
|
|
|
|
* through history and returning the first commit whose message starts
|
2010-08-13 05:32:49 +04:00
|
|
|
* the given regular expression.
|
2010-08-03 01:37:06 +04:00
|
|
|
*
|
|
|
|
* For future extension, ':/!' is reserved. If you want to match a message
|
|
|
|
* beginning with a '!', you have to repeat the exclamation mark.
|
|
|
|
*/
|
|
|
|
#define ONELINE_SEEN (1u<<20)
|
|
|
|
|
2007-02-24 05:08:20 +03:00
|
|
|
static int handle_one_ref(const char *path,
|
|
|
|
const unsigned char *sha1, int flag, void *cb_data)
|
|
|
|
{
|
|
|
|
struct commit_list **list = cb_data;
|
|
|
|
struct object *object = parse_object(sha1);
|
|
|
|
if (!object)
|
|
|
|
return 0;
|
2008-02-18 10:31:54 +03:00
|
|
|
if (object->type == OBJ_TAG) {
|
2007-02-24 05:08:20 +03:00
|
|
|
object = deref_tag(object, path, strlen(path));
|
2008-02-18 10:31:54 +03:00
|
|
|
if (!object)
|
|
|
|
return 0;
|
|
|
|
}
|
2007-02-24 05:08:20 +03:00
|
|
|
if (object->type != OBJ_COMMIT)
|
|
|
|
return 0;
|
2010-11-27 04:58:14 +03:00
|
|
|
commit_list_insert_by_date((struct commit *)object, list);
|
2007-02-24 05:08:20 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-13 06:01:14 +03:00
|
|
|
static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
|
|
|
|
struct commit_list *list)
|
2007-02-24 05:08:20 +03:00
|
|
|
{
|
2010-12-13 06:01:14 +03:00
|
|
|
struct commit_list *backup = NULL, *l;
|
2010-12-13 09:19:00 +03:00
|
|
|
int found = 0;
|
2010-04-23 19:20:20 +04:00
|
|
|
regex_t regex;
|
2007-02-24 05:08:20 +03:00
|
|
|
|
|
|
|
if (prefix[0] == '!') {
|
|
|
|
if (prefix[1] != '!')
|
|
|
|
die ("Invalid search pattern: %s", prefix);
|
|
|
|
prefix++;
|
|
|
|
}
|
2010-04-23 19:20:20 +04:00
|
|
|
|
|
|
|
if (regcomp(®ex, prefix, REG_EXTENDED))
|
|
|
|
die("Invalid search pattern: %s", prefix);
|
|
|
|
|
2010-12-13 06:01:14 +03:00
|
|
|
for (l = list; l; l = l->next) {
|
|
|
|
l->item->object.flags |= ONELINE_SEEN;
|
2007-02-24 05:08:20 +03:00
|
|
|
commit_list_insert(l->item, &backup);
|
2010-12-13 06:01:14 +03:00
|
|
|
}
|
2007-03-11 21:49:08 +03:00
|
|
|
while (list) {
|
2010-12-13 09:19:00 +03:00
|
|
|
char *p, *to_free = NULL;
|
2007-03-12 21:30:38 +03:00
|
|
|
struct commit *commit;
|
2007-12-03 21:42:39 +03:00
|
|
|
enum object_type type;
|
|
|
|
unsigned long size;
|
2010-12-13 09:19:00 +03:00
|
|
|
int matches;
|
2007-03-11 21:49:08 +03:00
|
|
|
|
|
|
|
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
|
2008-02-18 23:47:53 +03:00
|
|
|
if (!parse_object(commit->object.sha1))
|
|
|
|
continue;
|
2007-12-03 21:42:39 +03:00
|
|
|
if (commit->buffer)
|
|
|
|
p = commit->buffer;
|
|
|
|
else {
|
|
|
|
p = read_sha1_file(commit->object.sha1, &type, &size);
|
|
|
|
if (!p)
|
|
|
|
continue;
|
2010-12-13 09:19:00 +03:00
|
|
|
to_free = p;
|
2007-12-03 21:42:39 +03:00
|
|
|
}
|
2010-12-13 09:19:00 +03:00
|
|
|
|
|
|
|
p = strstr(p, "\n\n");
|
|
|
|
matches = p && !regexec(®ex, p + 2, 0, NULL, 0);
|
|
|
|
free(to_free);
|
|
|
|
|
|
|
|
if (matches) {
|
2007-02-24 05:08:20 +03:00
|
|
|
hashcpy(sha1, commit->object.sha1);
|
2010-12-13 09:19:00 +03:00
|
|
|
found = 1;
|
2007-02-24 05:08:20 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-04-23 19:20:20 +04:00
|
|
|
regfree(®ex);
|
2007-02-24 05:08:20 +03:00
|
|
|
free_commit_list(list);
|
|
|
|
for (l = backup; l; l = l->next)
|
|
|
|
clear_commit_marks(l->item, ONELINE_SEEN);
|
2010-12-13 09:19:00 +03:00
|
|
|
free_commit_list(backup);
|
|
|
|
return found ? 0 : -1;
|
2007-02-24 05:08:20 +03:00
|
|
|
}
|
|
|
|
|
2009-01-17 19:09:53 +03:00
|
|
|
struct grab_nth_branch_switch_cbdata {
|
2013-03-09 01:27:37 +04:00
|
|
|
int remaining;
|
|
|
|
struct strbuf buf;
|
2009-01-17 19:09:53 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
|
|
|
|
const char *email, unsigned long timestamp, int tz,
|
|
|
|
const char *message, void *cb_data)
|
|
|
|
{
|
|
|
|
struct grab_nth_branch_switch_cbdata *cb = cb_data;
|
2009-01-17 19:09:54 +03:00
|
|
|
const char *match = NULL, *target = NULL;
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (!prefixcmp(message, "checkout: moving from ")) {
|
|
|
|
match = message + strlen("checkout: moving from ");
|
2009-01-21 11:37:38 +03:00
|
|
|
target = strstr(match, " to ");
|
2009-01-17 19:09:53 +03:00
|
|
|
}
|
|
|
|
|
2009-01-20 03:44:08 +03:00
|
|
|
if (!match || !target)
|
2009-01-17 19:09:53 +03:00
|
|
|
return 0;
|
2013-03-09 01:27:37 +04:00
|
|
|
if (--(cb->remaining) == 0) {
|
|
|
|
len = target - match;
|
|
|
|
strbuf_reset(&cb->buf);
|
|
|
|
strbuf_add(&cb->buf, match, len);
|
|
|
|
return 1; /* we are done */
|
|
|
|
}
|
2009-01-17 19:09:53 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-01-20 10:17:11 +03:00
|
|
|
* Parse @{-N} syntax, return the number of characters parsed
|
|
|
|
* if successful; otherwise signal an error with negative value.
|
2009-01-17 19:09:53 +03:00
|
|
|
*/
|
2010-01-20 10:17:11 +03:00
|
|
|
static int interpret_nth_prior_checkout(const char *name, struct strbuf *buf)
|
2009-01-17 19:09:53 +03:00
|
|
|
{
|
2009-01-19 11:04:25 +03:00
|
|
|
long nth;
|
2013-03-09 01:27:37 +04:00
|
|
|
int retval;
|
2009-01-17 19:09:53 +03:00
|
|
|
struct grab_nth_branch_switch_cbdata cb;
|
2009-01-17 19:09:54 +03:00
|
|
|
const char *brace;
|
|
|
|
char *num_end;
|
2009-01-17 19:09:53 +03:00
|
|
|
|
|
|
|
if (name[0] != '@' || name[1] != '{' || name[2] != '-')
|
|
|
|
return -1;
|
2009-01-17 19:09:54 +03:00
|
|
|
brace = strchr(name, '}');
|
|
|
|
if (!brace)
|
|
|
|
return -1;
|
2013-03-09 01:27:37 +04:00
|
|
|
nth = strtol(name + 3, &num_end, 10);
|
2009-01-17 19:09:54 +03:00
|
|
|
if (num_end != brace)
|
2009-01-17 19:09:53 +03:00
|
|
|
return -1;
|
2009-01-19 11:04:25 +03:00
|
|
|
if (nth <= 0)
|
|
|
|
return -1;
|
2013-03-09 01:27:37 +04:00
|
|
|
cb.remaining = nth;
|
|
|
|
strbuf_init(&cb.buf, 20);
|
|
|
|
|
2009-01-20 08:58:31 +03:00
|
|
|
retval = 0;
|
2013-03-09 01:27:37 +04:00
|
|
|
if (0 < for_each_reflog_ent_reverse("HEAD", grab_nth_branch_switch, &cb)) {
|
|
|
|
strbuf_reset(buf);
|
|
|
|
strbuf_add(buf, cb.buf.buf, cb.buf.len);
|
|
|
|
retval = brace - name + 1;
|
|
|
|
}
|
2009-01-17 19:09:54 +03:00
|
|
|
|
2013-03-09 01:27:37 +04:00
|
|
|
strbuf_release(&cb.buf);
|
2009-01-20 08:58:31 +03:00
|
|
|
return retval;
|
2009-01-17 19:09:53 +03:00
|
|
|
}
|
|
|
|
|
2009-10-18 23:34:56 +04:00
|
|
|
int get_sha1_mb(const char *name, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct commit *one, *two;
|
|
|
|
struct commit_list *mbs;
|
|
|
|
unsigned char sha1_tmp[20];
|
|
|
|
const char *dots;
|
|
|
|
int st;
|
|
|
|
|
|
|
|
dots = strstr(name, "...");
|
|
|
|
if (!dots)
|
|
|
|
return get_sha1(name, sha1);
|
|
|
|
if (dots == name)
|
|
|
|
st = get_sha1("HEAD", sha1_tmp);
|
|
|
|
else {
|
|
|
|
struct strbuf sb;
|
|
|
|
strbuf_init(&sb, dots - name);
|
|
|
|
strbuf_add(&sb, name, dots - name);
|
2012-07-02 23:04:52 +04:00
|
|
|
st = get_sha1_committish(sb.buf, sha1_tmp);
|
2009-10-18 23:34:56 +04:00
|
|
|
strbuf_release(&sb);
|
|
|
|
}
|
|
|
|
if (st)
|
|
|
|
return st;
|
|
|
|
one = lookup_commit_reference_gently(sha1_tmp, 0);
|
|
|
|
if (!one)
|
|
|
|
return -1;
|
|
|
|
|
2012-07-02 23:04:52 +04:00
|
|
|
if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
|
2009-10-18 23:34:56 +04:00
|
|
|
return -1;
|
|
|
|
two = lookup_commit_reference_gently(sha1_tmp, 0);
|
|
|
|
if (!two)
|
|
|
|
return -1;
|
|
|
|
mbs = get_merge_bases(one, two, 1);
|
|
|
|
if (!mbs || mbs->next)
|
|
|
|
st = -1;
|
|
|
|
else {
|
|
|
|
st = 0;
|
|
|
|
hashcpy(sha1, mbs->item->object.sha1);
|
|
|
|
}
|
|
|
|
free_commit_list(mbs);
|
|
|
|
return st;
|
|
|
|
}
|
|
|
|
|
Add new @ shortcut for HEAD
Typing 'HEAD' is tedious, especially when we can use '@' instead.
The reason for choosing '@' is that it follows naturally from the
ref@op syntax (e.g. HEAD@{u}), except we have no ref, and no
operation, and when we don't have those, it makes sens to assume
'HEAD'.
So now we can use 'git show @~1', and all that goody goodness.
Until now '@' was a valid name, but it conflicts with this idea, so
let's make it invalid. Probably very few people, if any, used this name.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-05-08 02:04:31 +04:00
|
|
|
/* parse @something syntax, when 'something' is not {.*} */
|
|
|
|
static int interpret_empty_at(const char *name, int namelen, int len, struct strbuf *buf)
|
|
|
|
{
|
|
|
|
if (len || name[1] == '{')
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
strbuf_reset(buf);
|
|
|
|
strbuf_add(buf, "HEAD", 4);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2013-05-08 02:04:30 +04:00
|
|
|
static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
|
|
|
|
{
|
|
|
|
/* we have extra data, which might need further processing */
|
|
|
|
struct strbuf tmp = STRBUF_INIT;
|
|
|
|
int used = buf->len;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
strbuf_add(buf, name + len, namelen - len);
|
|
|
|
ret = interpret_branch_name(buf->buf, &tmp);
|
|
|
|
/* that data was not interpreted, remove our cruft */
|
|
|
|
if (ret < 0) {
|
|
|
|
strbuf_setlen(buf, used);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
strbuf_reset(buf);
|
|
|
|
strbuf_addbuf(buf, &tmp);
|
|
|
|
strbuf_release(&tmp);
|
|
|
|
/* tweak for size of {-N} versus expanded ref name */
|
|
|
|
return ret - used + len;
|
|
|
|
}
|
|
|
|
|
2010-01-20 10:17:11 +03:00
|
|
|
/*
|
|
|
|
* This reads short-hand syntax that not only evaluates to a commit
|
|
|
|
* object name, but also can act as if the end user spelled the name
|
|
|
|
* of the branch from the command line.
|
|
|
|
*
|
|
|
|
* - "@{-N}" finds the name of the Nth previous branch we were on, and
|
|
|
|
* places the name of the branch in the given buf and returns the
|
|
|
|
* number of characters parsed if successful.
|
|
|
|
*
|
|
|
|
* - "<branch>@{upstream}" finds the name of the other ref that
|
|
|
|
* <branch> is configured to merge with (missing <branch> defaults
|
|
|
|
* to the current branch), and places the name of the branch in the
|
|
|
|
* given buf and returns the number of characters parsed if
|
|
|
|
* successful.
|
|
|
|
*
|
|
|
|
* If the input is not of the accepted format, it returns a negative
|
|
|
|
* number to signal an error.
|
|
|
|
*
|
|
|
|
* If the input was ok but there are not N branch switches in the
|
|
|
|
* reflog, it returns 0.
|
|
|
|
*/
|
|
|
|
int interpret_branch_name(const char *name, struct strbuf *buf)
|
|
|
|
{
|
|
|
|
char *cp;
|
|
|
|
struct branch *upstream;
|
|
|
|
int namelen = strlen(name);
|
|
|
|
int len = interpret_nth_prior_checkout(name, buf);
|
|
|
|
int tmp_len;
|
|
|
|
|
2013-05-01 01:49:11 +04:00
|
|
|
if (!len) {
|
2010-01-20 10:17:11 +03:00
|
|
|
return len; /* syntax Ok, not enough switches */
|
2013-05-01 01:49:11 +04:00
|
|
|
} else if (len > 0) {
|
|
|
|
if (len == namelen)
|
|
|
|
return len; /* consumed all */
|
|
|
|
else
|
|
|
|
return reinterpret(name, namelen, len, buf);
|
2010-01-28 12:52:22 +03:00
|
|
|
}
|
|
|
|
|
2010-01-20 10:17:11 +03:00
|
|
|
cp = strchr(name, '@');
|
|
|
|
if (!cp)
|
|
|
|
return -1;
|
Add new @ shortcut for HEAD
Typing 'HEAD' is tedious, especially when we can use '@' instead.
The reason for choosing '@' is that it follows naturally from the
ref@op syntax (e.g. HEAD@{u}), except we have no ref, and no
operation, and when we don't have those, it makes sens to assume
'HEAD'.
So now we can use 'git show @~1', and all that goody goodness.
Until now '@' was a valid name, but it conflicts with this idea, so
let's make it invalid. Probably very few people, if any, used this name.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-05-08 02:04:31 +04:00
|
|
|
|
|
|
|
len = interpret_empty_at(name, namelen, cp - name, buf);
|
|
|
|
if (len > 0)
|
|
|
|
return reinterpret(name, namelen, len, buf);
|
|
|
|
|
2010-01-20 10:17:11 +03:00
|
|
|
tmp_len = upstream_mark(cp, namelen - (cp - name));
|
|
|
|
if (!tmp_len)
|
|
|
|
return -1;
|
Add new @ shortcut for HEAD
Typing 'HEAD' is tedious, especially when we can use '@' instead.
The reason for choosing '@' is that it follows naturally from the
ref@op syntax (e.g. HEAD@{u}), except we have no ref, and no
operation, and when we don't have those, it makes sens to assume
'HEAD'.
So now we can use 'git show @~1', and all that goody goodness.
Until now '@' was a valid name, but it conflicts with this idea, so
let's make it invalid. Probably very few people, if any, used this name.
Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-05-08 02:04:31 +04:00
|
|
|
|
2010-01-20 10:17:11 +03:00
|
|
|
len = cp + tmp_len - name;
|
|
|
|
cp = xstrndup(name, cp - name);
|
|
|
|
upstream = branch_get(*cp ? cp : NULL);
|
2012-04-14 11:54:32 +04:00
|
|
|
/*
|
|
|
|
* Upstream can be NULL only if cp refers to HEAD and HEAD
|
|
|
|
* points to something different than a branch.
|
|
|
|
*/
|
|
|
|
if (!upstream)
|
sha1_name: fix error message for @{u}
Currently, when no (valid) upstream is configured for a branch, you get
an error like:
$ git show @{u}
error: No upstream configured for branch 'upstream-error'
error: No upstream configured for branch 'upstream-error'
fatal: ambiguous argument '@{u}': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
The "error: " line actually appears twice, and the rest of the error
message is useless. In sha1_name.c:interpret_branch_name(), there is
really no point in processing further if @{u} couldn't be resolved, and
we might as well die() instead of returning an error(). After making
this change, you get:
$ git show @{u}
fatal: No upstream configured for branch 'upstream-error'
Also tweak a few tests in t1507 to expect this output.
This only turns error() that may be called after we know we are
dealing with an @{upstream} marker into die(), without touching
silent error returns "return -1" from the function. Any caller that
wants to handle an error condition itself will not be hurt by this
change, unless they want to see the message from error() and then
exit silently without giving its own message, which needs to be
fixed anyway.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-05-22 14:39:54 +04:00
|
|
|
die(_("HEAD does not point to a branch"));
|
2012-04-14 11:54:33 +04:00
|
|
|
if (!upstream->merge || !upstream->merge[0]->dst) {
|
|
|
|
if (!ref_exists(upstream->refname))
|
sha1_name: fix error message for @{u}
Currently, when no (valid) upstream is configured for a branch, you get
an error like:
$ git show @{u}
error: No upstream configured for branch 'upstream-error'
error: No upstream configured for branch 'upstream-error'
fatal: ambiguous argument '@{u}': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
The "error: " line actually appears twice, and the rest of the error
message is useless. In sha1_name.c:interpret_branch_name(), there is
really no point in processing further if @{u} couldn't be resolved, and
we might as well die() instead of returning an error(). After making
this change, you get:
$ git show @{u}
fatal: No upstream configured for branch 'upstream-error'
Also tweak a few tests in t1507 to expect this output.
This only turns error() that may be called after we know we are
dealing with an @{upstream} marker into die(), without touching
silent error returns "return -1" from the function. Any caller that
wants to handle an error condition itself will not be hurt by this
change, unless they want to see the message from error() and then
exit silently without giving its own message, which needs to be
fixed anyway.
Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-05-22 14:39:54 +04:00
|
|
|
die(_("No such branch: '%s'"), cp);
|
|
|
|
if (!upstream->merge) {
|
|
|
|
die(_("No upstream configured for branch '%s'"),
|
|
|
|
upstream->name);
|
|
|
|
}
|
|
|
|
die(
|
2012-04-14 11:54:35 +04:00
|
|
|
_("Upstream branch '%s' not stored as a remote-tracking branch"),
|
2012-04-14 11:54:34 +04:00
|
|
|
upstream->merge[0]->src);
|
2012-04-14 11:54:33 +04:00
|
|
|
}
|
2010-01-20 10:17:11 +03:00
|
|
|
free(cp);
|
|
|
|
cp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);
|
|
|
|
strbuf_reset(buf);
|
|
|
|
strbuf_addstr(buf, cp);
|
|
|
|
free(cp);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2010-11-06 14:46:52 +03:00
|
|
|
int strbuf_branchname(struct strbuf *sb, const char *name)
|
|
|
|
{
|
|
|
|
int len = strlen(name);
|
2013-05-16 01:32:30 +04:00
|
|
|
int used = interpret_branch_name(name, sb);
|
|
|
|
|
|
|
|
if (used == len)
|
2010-11-06 14:46:52 +03:00
|
|
|
return 0;
|
2013-05-16 01:32:30 +04:00
|
|
|
if (used < 0)
|
|
|
|
used = 0;
|
|
|
|
strbuf_add(sb, name + used, len - used);
|
2010-11-06 14:46:52 +03:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
|
|
|
|
{
|
|
|
|
strbuf_branchname(sb, name);
|
|
|
|
if (name[0] == '-')
|
2011-09-16 01:10:25 +04:00
|
|
|
return -1;
|
2010-11-06 14:46:52 +03:00
|
|
|
strbuf_splice(sb, 0, 0, "refs/heads/", 11);
|
2011-09-16 01:10:25 +04:00
|
|
|
return check_refname_format(sb->buf, 0);
|
2010-11-06 14:46:52 +03:00
|
|
|
}
|
|
|
|
|
2005-08-04 09:15:49 +04:00
|
|
|
/*
|
|
|
|
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
|
|
|
|
* notably "xyz^" for "parent of xyz"
|
|
|
|
*/
|
|
|
|
int get_sha1(const char *name, unsigned char *sha1)
|
|
|
|
{
|
2010-06-09 21:02:06 +04:00
|
|
|
struct object_context unused;
|
2012-07-02 21:32:11 +04:00
|
|
|
return get_sha1_with_context(name, 0, sha1, &unused);
|
2007-04-24 00:55:05 +04:00
|
|
|
}
|
|
|
|
|
2012-07-02 23:04:52 +04:00
|
|
|
/*
|
|
|
|
* Many callers know that the user meant to name a committish by
|
|
|
|
* syntactical positions where the object name appears. Calling this
|
|
|
|
* function allows the machinery to disambiguate shorter-than-unique
|
|
|
|
* abbreviated object names between committish and others.
|
|
|
|
*
|
|
|
|
* Note that this does NOT error out when the named object is not a
|
|
|
|
* committish. It is merely to give a hint to the disambiguation
|
|
|
|
* machinery.
|
|
|
|
*/
|
|
|
|
int get_sha1_committish(const char *name, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct object_context unused;
|
|
|
|
return get_sha1_with_context(name, GET_SHA1_COMMITTISH,
|
|
|
|
sha1, &unused);
|
|
|
|
}
|
|
|
|
|
2012-07-03 10:35:05 +04:00
|
|
|
int get_sha1_treeish(const char *name, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct object_context unused;
|
|
|
|
return get_sha1_with_context(name, GET_SHA1_TREEISH,
|
|
|
|
sha1, &unused);
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_sha1_commit(const char *name, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct object_context unused;
|
|
|
|
return get_sha1_with_context(name, GET_SHA1_COMMIT,
|
|
|
|
sha1, &unused);
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_sha1_tree(const char *name, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct object_context unused;
|
|
|
|
return get_sha1_with_context(name, GET_SHA1_TREE,
|
|
|
|
sha1, &unused);
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_sha1_blob(const char *name, unsigned char *sha1)
|
|
|
|
{
|
|
|
|
struct object_context unused;
|
|
|
|
return get_sha1_with_context(name, GET_SHA1_BLOB,
|
|
|
|
sha1, &unused);
|
2007-04-24 00:55:05 +04:00
|
|
|
}
|
|
|
|
|
2009-12-07 13:10:50 +03:00
|
|
|
/* Must be called only when object_name:filename doesn't exist. */
|
|
|
|
static void diagnose_invalid_sha1_path(const char *prefix,
|
|
|
|
const char *filename,
|
|
|
|
const unsigned char *tree_sha1,
|
2013-03-16 22:29:31 +04:00
|
|
|
const char *object_name,
|
|
|
|
int object_name_len)
|
2009-12-07 13:10:50 +03:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
unsigned char sha1[20];
|
|
|
|
unsigned mode;
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
|
|
|
|
|
|
|
if (!lstat(filename, &st))
|
2013-03-16 22:29:31 +04:00
|
|
|
die("Path '%s' exists on disk, but not in '%.*s'.",
|
|
|
|
filename, object_name_len, object_name);
|
2009-12-07 13:10:50 +03:00
|
|
|
if (errno == ENOENT || errno == ENOTDIR) {
|
|
|
|
char *fullname = xmalloc(strlen(filename)
|
|
|
|
+ strlen(prefix) + 1);
|
|
|
|
strcpy(fullname, prefix);
|
|
|
|
strcat(fullname, filename);
|
|
|
|
|
|
|
|
if (!get_tree_entry(tree_sha1, fullname,
|
|
|
|
sha1, &mode)) {
|
|
|
|
die("Path '%s' exists, but not '%s'.\n"
|
2013-03-16 22:29:31 +04:00
|
|
|
"Did you mean '%.*s:%s' aka '%.*s:./%s'?",
|
2009-12-07 13:10:50 +03:00
|
|
|
fullname,
|
|
|
|
filename,
|
2013-03-16 22:29:31 +04:00
|
|
|
object_name_len, object_name,
|
2011-03-31 13:17:34 +04:00
|
|
|
fullname,
|
2013-03-16 22:29:31 +04:00
|
|
|
object_name_len, object_name,
|
2011-03-31 13:17:34 +04:00
|
|
|
filename);
|
2009-12-07 13:10:50 +03:00
|
|
|
}
|
2013-03-16 22:29:31 +04:00
|
|
|
die("Path '%s' does not exist in '%.*s'",
|
|
|
|
filename, object_name_len, object_name);
|
2009-12-07 13:10:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Must be called only when :stage:filename doesn't exist. */
|
|
|
|
static void diagnose_invalid_index_path(int stage,
|
|
|
|
const char *prefix,
|
|
|
|
const char *filename)
|
|
|
|
{
|
|
|
|
struct stat st;
|
Convert "struct cache_entry *" to "const ..." wherever possible
I attempted to make index_state->cache[] a "const struct cache_entry **"
to find out how existing entries in index are modified and where. The
question I have is what do we do if we really need to keep track of on-disk
changes in the index. The result is
- diff-lib.c: setting CE_UPTODATE
- name-hash.c: setting CE_HASHED
- preload-index.c, read-cache.c, unpack-trees.c and
builtin/update-index: obvious
- entry.c: write_entry() may refresh the checked out entry via
fill_stat_cache_info(). This causes "non-const struct cache_entry
*" in builtin/apply.c, builtin/checkout-index.c and
builtin/checkout.c
- builtin/ls-files.c: --with-tree changes stagemask and may set
CE_UPDATE
Of these, write_entry() and its call sites are probably most
interesting because it modifies on-disk info. But this is stat info
and can be retrieved via refresh, at least for porcelain
commands. Other just uses ce_flags for local purposes.
So, keeping track of "dirty" entries is just a matter of setting a
flag in index modification functions exposed by read-cache.c. Except
unpack-trees, the rest of the code base does not do anything funny
behind read-cache's back.
The actual patch is less valueable than the summary above. But if
anyone wants to re-identify the above sites. Applying this patch, then
this:
diff --git a/cache.h b/cache.h
index 430d021..1692891 100644
--- a/cache.h
+++ b/cache.h
@@ -267,7 +267,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
struct index_state {
- struct cache_entry **cache;
+ const struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
will help quickly identify them without bogus warnings.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 19:29:00 +04:00
|
|
|
const struct cache_entry *ce;
|
2009-12-07 13:10:50 +03:00
|
|
|
int pos;
|
|
|
|
unsigned namelen = strlen(filename);
|
|
|
|
unsigned fullnamelen;
|
|
|
|
char *fullname;
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
prefix = "";
|
|
|
|
|
|
|
|
/* Wrong stage number? */
|
|
|
|
pos = cache_name_pos(filename, namelen);
|
|
|
|
if (pos < 0)
|
|
|
|
pos = -pos - 1;
|
2010-02-28 18:49:15 +03:00
|
|
|
if (pos < active_nr) {
|
|
|
|
ce = active_cache[pos];
|
|
|
|
if (ce_namelen(ce) == namelen &&
|
|
|
|
!memcmp(ce->name, filename, namelen))
|
|
|
|
die("Path '%s' is in the index, but not at stage %d.\n"
|
|
|
|
"Did you mean ':%d:%s'?",
|
|
|
|
filename, stage,
|
|
|
|
ce_stage(ce), filename);
|
|
|
|
}
|
2009-12-07 13:10:50 +03:00
|
|
|
|
|
|
|
/* Confusion between relative and absolute filenames? */
|
|
|
|
fullnamelen = namelen + strlen(prefix);
|
|
|
|
fullname = xmalloc(fullnamelen + 1);
|
|
|
|
strcpy(fullname, prefix);
|
|
|
|
strcat(fullname, filename);
|
|
|
|
pos = cache_name_pos(fullname, fullnamelen);
|
|
|
|
if (pos < 0)
|
|
|
|
pos = -pos - 1;
|
2010-02-28 18:49:15 +03:00
|
|
|
if (pos < active_nr) {
|
|
|
|
ce = active_cache[pos];
|
|
|
|
if (ce_namelen(ce) == fullnamelen &&
|
|
|
|
!memcmp(ce->name, fullname, fullnamelen))
|
|
|
|
die("Path '%s' is in the index, but not '%s'.\n"
|
2011-03-31 13:17:34 +04:00
|
|
|
"Did you mean ':%d:%s' aka ':%d:./%s'?",
|
2010-02-28 18:49:15 +03:00
|
|
|
fullname, filename,
|
2011-03-31 13:17:34 +04:00
|
|
|
ce_stage(ce), fullname,
|
|
|
|
ce_stage(ce), filename);
|
2010-02-28 18:49:15 +03:00
|
|
|
}
|
2009-12-07 13:10:50 +03:00
|
|
|
|
|
|
|
if (!lstat(filename, &st))
|
|
|
|
die("Path '%s' exists on disk, but not in the index.", filename);
|
|
|
|
if (errno == ENOENT || errno == ENOTDIR)
|
|
|
|
die("Path '%s' does not exist (neither on disk nor in the index).",
|
|
|
|
filename);
|
|
|
|
|
|
|
|
free(fullname);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-28 06:37:32 +03:00
|
|
|
static char *resolve_relative_path(const char *rel)
|
|
|
|
{
|
|
|
|
if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!startup_info)
|
|
|
|
die("BUG: startup_info struct is not initialized.");
|
|
|
|
|
|
|
|
if (!is_inside_work_tree())
|
|
|
|
die("relative path syntax can't be used outside working tree.");
|
|
|
|
|
|
|
|
/* die() inside prefix_path() if resolved path is outside worktree */
|
|
|
|
return prefix_path(startup_info->prefix,
|
|
|
|
startup_info->prefix ? strlen(startup_info->prefix) : 0,
|
|
|
|
rel);
|
|
|
|
}
|
|
|
|
|
2012-07-02 21:32:11 +04:00
|
|
|
static int get_sha1_with_context_1(const char *name,
|
|
|
|
unsigned flags,
|
|
|
|
const char *prefix,
|
|
|
|
unsigned char *sha1,
|
|
|
|
struct object_context *oc)
|
2007-04-24 00:55:05 +04:00
|
|
|
{
|
|
|
|
int ret, bracket_depth;
|
2006-04-22 04:31:04 +04:00
|
|
|
int namelen = strlen(name);
|
|
|
|
const char *cp;
|
2012-07-02 21:32:11 +04:00
|
|
|
int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
|
2006-04-19 03:45:16 +04:00
|
|
|
|
2010-06-09 21:02:06 +04:00
|
|
|
memset(oc, 0, sizeof(*oc));
|
|
|
|
oc->mode = S_IFINVALID;
|
2012-07-02 21:32:11 +04:00
|
|
|
ret = get_sha1_1(name, namelen, sha1, flags);
|
2006-04-22 04:31:04 +04:00
|
|
|
if (!ret)
|
|
|
|
return ret;
|
2012-07-02 21:32:11 +04:00
|
|
|
/*
|
|
|
|
* sha1:path --> object name of path in ent sha1
|
2010-11-28 06:37:32 +03:00
|
|
|
* :path -> object name of absolute path in index
|
|
|
|
* :./path -> object name of path relative to cwd in index
|
2006-04-22 04:31:04 +04:00
|
|
|
* :[0-3]:path -> object name of path in index at stage
|
2010-09-24 20:43:59 +04:00
|
|
|
* :/foo -> recent commit matching foo
|
2006-04-22 04:31:04 +04:00
|
|
|
*/
|
|
|
|
if (name[0] == ':') {
|
|
|
|
int stage = 0;
|
Convert "struct cache_entry *" to "const ..." wherever possible
I attempted to make index_state->cache[] a "const struct cache_entry **"
to find out how existing entries in index are modified and where. The
question I have is what do we do if we really need to keep track of on-disk
changes in the index. The result is
- diff-lib.c: setting CE_UPTODATE
- name-hash.c: setting CE_HASHED
- preload-index.c, read-cache.c, unpack-trees.c and
builtin/update-index: obvious
- entry.c: write_entry() may refresh the checked out entry via
fill_stat_cache_info(). This causes "non-const struct cache_entry
*" in builtin/apply.c, builtin/checkout-index.c and
builtin/checkout.c
- builtin/ls-files.c: --with-tree changes stagemask and may set
CE_UPDATE
Of these, write_entry() and its call sites are probably most
interesting because it modifies on-disk info. But this is stat info
and can be retrieved via refresh, at least for porcelain
commands. Other just uses ce_flags for local purposes.
So, keeping track of "dirty" entries is just a matter of setting a
flag in index modification functions exposed by read-cache.c. Except
unpack-trees, the rest of the code base does not do anything funny
behind read-cache's back.
The actual patch is less valueable than the summary above. But if
anyone wants to re-identify the above sites. Applying this patch, then
this:
diff --git a/cache.h b/cache.h
index 430d021..1692891 100644
--- a/cache.h
+++ b/cache.h
@@ -267,7 +267,7 @@ static inline unsigned int canon_mode(unsigned int mode)
#define cache_entry_size(len) (offsetof(struct cache_entry,name) + (len) + 1)
struct index_state {
- struct cache_entry **cache;
+ const struct cache_entry **cache;
unsigned int version;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
will help quickly identify them without bogus warnings.
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2013-07-09 19:29:00 +04:00
|
|
|
const struct cache_entry *ce;
|
2010-11-28 06:37:32 +03:00
|
|
|
char *new_path = NULL;
|
2006-04-22 04:31:04 +04:00
|
|
|
int pos;
|
fix overslow :/no-such-string-ever-existed diagnostics
"git cmd :/no-such-string-ever-existed" runs an extra round of get_sha1()
since 009fee4 (Detailed diagnosis when parsing an object name fails.,
2009-12-07). Once without error diagnosis to see there is no commit with
such a string in the log message (hence "it cannot be a ref"), and after
seeing that :/no-such-string-ever-existed is not a filename (hence "it
cannot be a path, either"), another time to give "better diagnosis".
The thing is, the second time it runs, we already know that traversing the
history all the way down to the root will _not_ find any matching commit.
Rename misguided "gently" parameter, which is turned off _only_ when the
"detailed diagnosis" codepath knows that it cannot be a ref and making the
call only for the caller to die with a message. Flip its meaning (and
adjust the callers) and call it "only_to_die", which is not a great name,
but it describes far more clearly what the codepaths that switches their
behaviour based on this variable do.
On my box, the command spends ~1.8 seconds without the patch to make the
report; with the patch it spends ~1.12 seconds.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-10 23:02:54 +04:00
|
|
|
if (!only_to_die && namelen > 2 && name[1] == '/') {
|
2010-12-13 06:01:14 +03:00
|
|
|
struct commit_list *list = NULL;
|
|
|
|
for_each_ref(handle_one_ref, &list);
|
|
|
|
return get_sha1_oneline(name + 2, sha1, list);
|
|
|
|
}
|
2006-04-22 04:31:04 +04:00
|
|
|
if (namelen < 3 ||
|
|
|
|
name[2] != ':' ||
|
|
|
|
name[1] < '0' || '3' < name[1])
|
|
|
|
cp = name + 1;
|
|
|
|
else {
|
|
|
|
stage = name[1] - '0';
|
|
|
|
cp = name + 3;
|
2006-04-19 03:45:16 +04:00
|
|
|
}
|
2010-12-10 00:38:05 +03:00
|
|
|
new_path = resolve_relative_path(cp);
|
|
|
|
if (!new_path) {
|
|
|
|
namelen = namelen - (cp - name);
|
|
|
|
} else {
|
|
|
|
cp = new_path;
|
|
|
|
namelen = strlen(cp);
|
|
|
|
}
|
2010-06-09 21:02:06 +04:00
|
|
|
|
|
|
|
strncpy(oc->path, cp,
|
|
|
|
sizeof(oc->path));
|
|
|
|
oc->path[sizeof(oc->path)-1] = '\0';
|
|
|
|
|
2006-04-22 04:31:04 +04:00
|
|
|
if (!active_cache)
|
|
|
|
read_cache();
|
|
|
|
pos = cache_name_pos(cp, namelen);
|
|
|
|
if (pos < 0)
|
|
|
|
pos = -pos - 1;
|
|
|
|
while (pos < active_nr) {
|
|
|
|
ce = active_cache[pos];
|
|
|
|
if (ce_namelen(ce) != namelen ||
|
|
|
|
memcmp(ce->name, cp, namelen))
|
|
|
|
break;
|
|
|
|
if (ce_stage(ce) == stage) {
|
2006-08-23 10:49:00 +04:00
|
|
|
hashcpy(sha1, ce->sha1);
|
2010-09-29 15:35:24 +04:00
|
|
|
oc->mode = ce->ce_mode;
|
2010-11-28 06:37:32 +03:00
|
|
|
free(new_path);
|
2006-04-22 04:31:04 +04:00
|
|
|
return 0;
|
|
|
|
}
|
2006-05-09 02:44:06 +04:00
|
|
|
pos++;
|
2006-04-22 04:31:04 +04:00
|
|
|
}
|
fix overslow :/no-such-string-ever-existed diagnostics
"git cmd :/no-such-string-ever-existed" runs an extra round of get_sha1()
since 009fee4 (Detailed diagnosis when parsing an object name fails.,
2009-12-07). Once without error diagnosis to see there is no commit with
such a string in the log message (hence "it cannot be a ref"), and after
seeing that :/no-such-string-ever-existed is not a filename (hence "it
cannot be a path, either"), another time to give "better diagnosis".
The thing is, the second time it runs, we already know that traversing the
history all the way down to the root will _not_ find any matching commit.
Rename misguided "gently" parameter, which is turned off _only_ when the
"detailed diagnosis" codepath knows that it cannot be a ref and making the
call only for the caller to die with a message. Flip its meaning (and
adjust the callers) and call it "only_to_die", which is not a great name,
but it describes far more clearly what the codepaths that switches their
behaviour based on this variable do.
On my box, the command spends ~1.8 seconds without the patch to make the
report; with the patch it spends ~1.12 seconds.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-10 23:02:54 +04:00
|
|
|
if (only_to_die && name[1] && name[1] != '/')
|
2009-12-07 13:10:50 +03:00
|
|
|
diagnose_invalid_index_path(stage, prefix, cp);
|
2010-11-28 06:37:32 +03:00
|
|
|
free(new_path);
|
2006-04-22 04:31:04 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2006-05-19 11:29:43 +04:00
|
|
|
for (cp = name, bracket_depth = 0; *cp; cp++) {
|
|
|
|
if (*cp == '{')
|
|
|
|
bracket_depth++;
|
|
|
|
else if (bracket_depth && *cp == '}')
|
|
|
|
bracket_depth--;
|
|
|
|
else if (!bracket_depth && *cp == ':')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (*cp == ':') {
|
2006-04-22 04:31:04 +04:00
|
|
|
unsigned char tree_sha1[20];
|
2013-03-16 22:29:31 +04:00
|
|
|
int len = cp - name;
|
|
|
|
if (!get_sha1_1(name, len, tree_sha1, GET_SHA1_TREEISH)) {
|
2009-12-07 13:10:50 +03:00
|
|
|
const char *filename = cp+1;
|
2010-11-28 06:37:32 +03:00
|
|
|
char *new_filename = NULL;
|
|
|
|
|
|
|
|
new_filename = resolve_relative_path(filename);
|
|
|
|
if (new_filename)
|
|
|
|
filename = new_filename;
|
2010-06-09 21:02:06 +04:00
|
|
|
ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
|
2012-06-18 22:18:20 +04:00
|
|
|
if (ret && only_to_die) {
|
2009-12-07 13:10:50 +03:00
|
|
|
diagnose_invalid_sha1_path(prefix, filename,
|
2013-03-16 22:29:31 +04:00
|
|
|
tree_sha1,
|
|
|
|
name, len);
|
2009-12-07 13:10:50 +03:00
|
|
|
}
|
2010-06-09 21:02:06 +04:00
|
|
|
hashcpy(oc->tree, tree_sha1);
|
|
|
|
strncpy(oc->path, filename,
|
|
|
|
sizeof(oc->path));
|
|
|
|
oc->path[sizeof(oc->path)-1] = '\0';
|
|
|
|
|
2010-11-28 06:37:32 +03:00
|
|
|
free(new_filename);
|
2009-12-07 13:10:50 +03:00
|
|
|
return ret;
|
|
|
|
} else {
|
fix overslow :/no-such-string-ever-existed diagnostics
"git cmd :/no-such-string-ever-existed" runs an extra round of get_sha1()
since 009fee4 (Detailed diagnosis when parsing an object name fails.,
2009-12-07). Once without error diagnosis to see there is no commit with
such a string in the log message (hence "it cannot be a ref"), and after
seeing that :/no-such-string-ever-existed is not a filename (hence "it
cannot be a path, either"), another time to give "better diagnosis".
The thing is, the second time it runs, we already know that traversing the
history all the way down to the root will _not_ find any matching commit.
Rename misguided "gently" parameter, which is turned off _only_ when the
"detailed diagnosis" codepath knows that it cannot be a ref and making the
call only for the caller to die with a message. Flip its meaning (and
adjust the callers) and call it "only_to_die", which is not a great name,
but it describes far more clearly what the codepaths that switches their
behaviour based on this variable do.
On my box, the command spends ~1.8 seconds without the patch to make the
report; with the patch it spends ~1.12 seconds.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2011-05-10 23:02:54 +04:00
|
|
|
if (only_to_die)
|
2013-03-16 22:29:31 +04:00
|
|
|
die("Invalid object name '%.*s'.", len, name);
|
2009-12-07 13:10:50 +03:00
|
|
|
}
|
2006-04-19 03:45:16 +04:00
|
|
|
}
|
|
|
|
return ret;
|
2005-08-04 09:15:49 +04:00
|
|
|
}
|
2012-07-02 21:19:35 +04:00
|
|
|
|
2012-07-02 22:01:25 +04:00
|
|
|
/*
|
|
|
|
* Call this function when you know "name" given by the end user must
|
|
|
|
* name an object but it doesn't; the function _may_ die with a better
|
|
|
|
* diagnostic message than "no such object 'name'", e.g. "Path 'doc' does not
|
|
|
|
* exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
|
|
|
|
* you have a chance to diagnose the error further.
|
|
|
|
*/
|
|
|
|
void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
|
2012-07-02 21:19:35 +04:00
|
|
|
{
|
|
|
|
struct object_context oc;
|
2012-07-02 22:01:25 +04:00
|
|
|
unsigned char sha1[20];
|
2012-07-02 21:32:11 +04:00
|
|
|
get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
|
2012-07-02 22:01:25 +04:00
|
|
|
}
|
|
|
|
|
2012-07-02 21:32:11 +04:00
|
|
|
int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
|
2012-07-02 21:19:35 +04:00
|
|
|
{
|
2012-07-02 21:32:11 +04:00
|
|
|
return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
|
2012-07-02 21:19:35 +04:00
|
|
|
}
|